--- 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"; const MAX_ITEMS = 10; interface LatestItemsEntry { type: string; 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 latestItems: LatestItemsEntry[] = await Promise.all( [ stories.map((story) => ({ date: story.data.pubDate, fn: async () => ({ type: "Story", 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", 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, })), ] .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-description" property="og:description" content="Bad Manners || Welcome to my gallery!" /> <h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">My gallery</h1> <div class="p-summary"> <p class="my-4"> Welcome to my gallery! You can expect lots of safe vore/endosoma ahead. Use the navigation menu to navigate through my content. </p> <ul class="list-disc pl-8"> <li><a class="text-link underline" href="/stories/1">Read my stories!</a></li> <li><a class="text-link underline" href="/games/crossing-over">Play my visual novel!</a></li> <li><a class="text-link underline" href="/tags">Find all content with a certain tag!</a></li> </ul> <p class="my-4"> For more information about me, please check out <a class="text-link underline" href="https://badmanners.xyz/" target="_blank">my main website</a >. </p> </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}> {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"> <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"> <p class="p-summary" aria-label="Summary"> {entry.altText} </p> <div aria-label="Authors"> {entry.authors.map((author) => ( <UserComponent rel="author" class="p-author" user={author} lang={entry.lang} /> ))} </div> </div> </li> )) } </ul> </section> </GalleryLayout>