182 lines
6.8 KiB
Text
182 lines
6.8 KiB
Text
---
|
|
import type { ImageMetadata } from "astro";
|
|
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
|
|
import { Image } from "astro:assets";
|
|
import GalleryLayout from "@layouts/GalleryLayout.astro";
|
|
import { t, type Lang } from "@i18n";
|
|
import UserComponent from "@components/UserComponent.astro";
|
|
import { markdownToPlaintext } from "@utils/markdown_to_plaintext";
|
|
import { IconNoOneUnder18, IconSquareRSS } from "@components/icons";
|
|
|
|
const MAX_ITEMS = 10;
|
|
|
|
interface LatestItemsEntry {
|
|
type: string;
|
|
isAgeRestricted: boolean;
|
|
thumbnail?: ImageMetadata;
|
|
href: string;
|
|
title: string;
|
|
lang: Lang;
|
|
authors: CollectionEntry<"users">[];
|
|
altText: string;
|
|
pubDate: Date;
|
|
}
|
|
|
|
type EntryWithPubDate<C extends CollectionKey> = CollectionEntry<C> & { data: { pubDate: Date } };
|
|
|
|
const stories = (
|
|
(await getCollection(
|
|
"stories",
|
|
(story) => !story.data.isDraft && story.data.pubDate,
|
|
)) as EntryWithPubDate<"stories">[]
|
|
)
|
|
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
|
|
.slice(0, MAX_ITEMS);
|
|
const games = (
|
|
(await getCollection("games", (game) => !game.data.isDraft && game.data.pubDate)) as EntryWithPubDate<"games">[]
|
|
)
|
|
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
|
|
.slice(0, MAX_ITEMS);
|
|
const posts = (
|
|
(await getCollection("blog", (post) => !post.data.isDraft && post.data.pubDate)) as EntryWithPubDate<"blog">[]
|
|
)
|
|
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
|
|
.slice(0, MAX_ITEMS);
|
|
|
|
const latestItems: LatestItemsEntry[] = await Promise.all(
|
|
[
|
|
stories.map((story) => ({
|
|
date: story.data.pubDate,
|
|
fn: async () =>
|
|
({
|
|
type: "Story",
|
|
isAgeRestricted: story.data.isAgeRestricted,
|
|
thumbnail: story.data.thumbnail,
|
|
href: `/stories/${story.slug}`,
|
|
title: story.data.title,
|
|
authors: await getEntries(story.data.authors),
|
|
lang: story.data.lang,
|
|
altText: t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning),
|
|
pubDate: story.data.pubDate,
|
|
}) satisfies LatestItemsEntry,
|
|
})),
|
|
games.map((game) => ({
|
|
date: game.data.pubDate,
|
|
fn: async () =>
|
|
({
|
|
type: "Game",
|
|
isAgeRestricted: game.data.isAgeRestricted,
|
|
thumbnail: game.data.thumbnail,
|
|
href: `/games/${game.slug}`,
|
|
title: game.data.title,
|
|
authors: await getEntries(game.data.authors),
|
|
lang: game.data.lang,
|
|
altText: t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning),
|
|
pubDate: game.data.pubDate,
|
|
}) satisfies LatestItemsEntry,
|
|
})),
|
|
posts.map((post) => ({
|
|
date: post.data.pubDate,
|
|
fn: async () =>
|
|
({
|
|
type: "Blog post",
|
|
isAgeRestricted: post.data.isAgeRestricted,
|
|
thumbnail: post.data.thumbnail,
|
|
href: `/blog/${post.slug}`,
|
|
title: post.data.title,
|
|
authors: await getEntries(post.data.authors),
|
|
lang: post.data.lang,
|
|
altText: markdownToPlaintext(post.data.description),
|
|
pubDate: post.data.pubDate,
|
|
}) satisfies LatestItemsEntry,
|
|
})),
|
|
]
|
|
.flat()
|
|
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
|
.slice(0, MAX_ITEMS)
|
|
.map(async (entry) => await entry.fn()),
|
|
);
|
|
---
|
|
|
|
<GalleryLayout pageTitle="Gallery" class="h-feed">
|
|
<meta slot="head" property="og:description" content="Bad Manners || Welcome to my gallery!" />
|
|
<h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Gallery</h1>
|
|
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
|
|
<div class="p-summary">
|
|
<div class="my-4 flex">
|
|
<p class="grow">
|
|
Hey there, welcome to my corner of the Internet! This is where I'll share all of the safe vore and endosoma
|
|
content that I'll make. You can check the latest uploads below, or use the navigation bar to dig through all of
|
|
my content.
|
|
</p>
|
|
<a class="u-url text-link ml-2 p-1 md:mr-5" href="/feed.xml" rel="alternate" title="RSS feed" data-tooltip>
|
|
<IconSquareRSS width="2rem" height="2rem" />
|
|
<span class="sr-only">RSS feed</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<section class="my-2" aria-labelledby="latest-uploads">
|
|
<h2 id="latest-uploads" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">Latest uploads</h2>
|
|
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
|
|
{
|
|
latestItems.map((entry) => (
|
|
<li class="h-entry break-inside-avoid" lang={entry.lang}>
|
|
<a
|
|
class="u-url text-link hover:underline focus:underline"
|
|
href={entry.href}
|
|
title={entry.altText}
|
|
data-tooltip
|
|
>
|
|
{entry.thumbnail ? (
|
|
<div class="flex aspect-square max-w-[192px] justify-center">
|
|
<Image
|
|
loading="eager"
|
|
class="u-photo m-auto"
|
|
src={entry.thumbnail}
|
|
alt={`Thumbnail for ${entry.title}`}
|
|
width={192}
|
|
/>
|
|
</div>
|
|
) : null}
|
|
<div class="max-w-[192px] text-sm">
|
|
<span class="p-name" aria-label="Title">
|
|
{entry.title}
|
|
</span>
|
|
<br />
|
|
<span class="italic">
|
|
{entry.isAgeRestricted ? (
|
|
<span class="inline-block align-middle" aria-label="Age restricted">
|
|
<IconNoOneUnder18 width="1.25rem" height="1.25rem" />
|
|
</span>
|
|
) : null}
|
|
<span class="p-category" aria-label="Category">
|
|
{entry.type}
|
|
</span>{" "}
|
|
–{" "}
|
|
<time
|
|
class="dt-published"
|
|
datetime={entry.pubDate.toISOString().slice(0, 10)}
|
|
aria-label="Publish date"
|
|
>
|
|
{entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
|
|
</time>
|
|
</span>
|
|
</div>
|
|
</a>
|
|
<div class="sr-only select-none">
|
|
<p class="p-summary" aria-label="Summary">
|
|
{entry.altText}
|
|
</p>
|
|
<div aria-label="Authors">
|
|
<span>{entry.authors.length == 1 ? "Author:" : "Authors:"}</span>
|
|
{entry.authors.map((author) => (
|
|
<UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</li>
|
|
))
|
|
}
|
|
</ul>
|
|
</section>
|
|
</GalleryLayout>
|