kemono2/client/src/pages/profile/fancards.tsx
2025-04-11 00:54:15 +02:00

124 lines
3.4 KiB
TypeScript

import { LoaderFunctionArgs, useLoaderData } from "react-router";
import { Helmet } from "@dr.pogodin/react-helmet";
import {
ICONS_PREPEND,
KEMONO_SITE,
SITE_NAME,
THUMBNAILS_PREPEND,
} from "#env/env-vars";
import { fetchFanboxProfileFancards } from "#api/profiles";
import { PageSkeleton } from "#components/pages";
import { validatePaysite } from "#entities/paysites";
import {
ProfileHeader,
Tabs,
IArtistDetails,
getArtist,
} from "#entities/profiles";
import { IFanCard } from "#entities/files";
interface IProps {
profile: IArtistDetails;
cards: IFanCard[];
}
export function FancardsPage() {
const { profile, cards } = useLoaderData() as IProps;
const title = "Fancards";
const heading = "Fancards";
return (
<PageSkeleton name="use" title={title} heading={heading}>
<Helmet>
<meta name="id" content={profile.id} />
<meta name="service" content={profile.service} />
<meta name="artist_name" content={profile.name} />
{/* <link rel="canonical" href="{{ g.canonical_url }}" /> */}
<meta property="og:title" content={title} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={SITE_NAME} />
<meta
property="og:image"
content={`${ICONS_PREPEND ?? KEMONO_SITE}/icons/${profile.service}/${
profile.id
}`}
/>
{/* <meta property="og:url" content="{{ g.canonical_url }}" /> */}
</Helmet>
<ProfileHeader
service={profile.service}
profileID={profile.id}
profileName={profile.name}
/>
<div className="paginator" id="paginator-top">
<Tabs
currentPage="fancards"
service={profile.service}
artistID={profile.id}
/>
<div id="fancard-container">
{cards.length === 0 ? (
<div className="no-results">
<h2 className="site-section__subheading">
Nobody here but us chickens!
</h2>
<p className="subtitle">There are no uploads for your query.</p>
</div>
) : (
cards.map((card) => (
<article key={card.id} className="fancard__file">
<span title={card.added.slice(0, 7)}>
Added {card.added.slice(0, 7)}
</span>
<a
className="fileThumb"
href={`${card.server ?? ""}${card.path}`}
>
<img
src={`${THUMBNAILS_PREPEND}/thumbnail/${card.path}`}
loading="lazy"
data-src={`${THUMBNAILS_PREPEND}/thumbnail/${card.path}`}
/>
</a>
</article>
))
)}
</div>
</div>
</PageSkeleton>
);
}
export async function loader({ params }: LoaderFunctionArgs): Promise<IProps> {
const service = params.service?.trim();
{
if (!service) {
throw new Error("Service name is required.");
}
validatePaysite(service);
if (service !== "fanbox") {
throw new Error(`Service must be "fanbox".`);
}
}
const profileID = params.creator_id?.trim();
{
if (!profileID) {
throw new Error("Artist ID is required.");
}
}
const profile = await getArtist(service, profileID);
const cards = await fetchFanboxProfileFancards(profileID);
return {
profile,
cards,
};
}