--- import type { GetStaticPaths, Page } from "astro"; import { Image } from "astro:assets"; import { getCollection, getEntries, type CollectionEntry } from "astro:content"; import GalleryLayout from "../../layouts/GalleryLayout.astro"; import { t } from "../../i18n"; import UserComponent from "../../components/UserComponent.astro"; type StoryWithPubDate = CollectionEntry<"stories"> & { data: { pubDate: Date } }; type Props = { page: Page<StoryWithPubDate & { authors: CollectionEntry<"users">[] }>; }; export const getStaticPaths: GetStaticPaths = async ({ paginate }) => { const stories = await Promise.all( ((await getCollection("stories", (story) => !story.data.isDraft && story.data.pubDate)) as StoryWithPubDate[]) .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime()) .map(async (story) => ({ ...story, authors: await getEntries(story.data.authors), })), ); return paginate(stories, { pageSize: 30 }) satisfies { props: Props }[]; }; const { page } = Astro.props; const totalPages = Math.ceil(page.total / page.size); --- <GalleryLayout pageTitle="Stories" class="h-feed"> <meta slot="head" property="og:description" content={`Bad Manners || ${page.total} stories.`} /> <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Stories</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"> <p class="my-4">The bulk of my content!</p> <p class="text-center font-light text-stone-950 dark:text-white"> { page.start === page.end ? `Displaying story #${page.start + 1}` : `Displaying stories #${page.start + 1}–${page.end + 1}` } / {page.total} </p> </div> <div class="mx-auto mb-6 mt-2 flex w-fit rounded-lg border border-stone-400 dark:border-stone-500"> { page.url.prev && ( <a class="text-link border-r border-stone-400 px-2 py-1 underline dark:border-stone-500" rel="prev" href={page.url.prev} > Previous page </a> ) } { [...Array(totalPages).keys()] .map((p) => p + 1) .map((p) => p === page.currentPage ? ( <span class="border-r border-stone-400 px-4 py-1 font-semibold text-stone-900 dark:border-stone-500 dark:text-stone-50"> {p} </span> ) : ( <a class="text-link border-r border-stone-400 px-2 py-1 underline dark:border-stone-500" href={page.url.current.replace(`/stories/${page.currentPage}`, `/stories/${p}`)} > {p} </a> ), ) } { page.url.next && ( <a class="text-link px-2 py-1 underline" rel="next" href={page.url.next}> Next page </a> ) } </div> <ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal"> { page.data.map((story, i) => ( <li class="h-entry break-inside-avoid" lang={story.data.lang}> <a class="u-url text-link hover:underline focus:underline" href={`/stories/${story.slug}`} title={t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)} data-tooltip > {story.data.thumbnail ? ( <div class="flex aspect-square max-w-[192px] justify-center"> <Image loading={i < 10 ? "eager" : "lazy"} class="u-photo m-auto" src={story.data.thumbnail} alt={`Thumbnail for ${story.data.title}`} width={192} /> </div> ) : null} <div class="max-w-[192px] text-sm"> <span class="p-name" aria-label="Title"> {story.data.title} </span> <br /> <time class="dt-published italic" datetime={story.data.pubDate.toISOString().slice(0, 10)} aria-label="Publish date" > {story.data.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })} </time> </div> </a> <div class="sr-only select-none"> <p class="p-category" aria-label="Category"> Story </p> <p class="p-summary" aria-label="Summary"> {t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)} </p> <div aria-label="Authors"> <span>{story.authors.length == 1 ? "Author:" : "Authors:"}</span> {story.authors.map((author) => ( <UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} /> ))} </div> </div> </li> )) } </ul> <div class="mx-auto my-6 flex w-fit rounded-lg border border-stone-400 dark:border-stone-500"> { page.url.prev && ( <a class="text-link border-r border-stone-400 px-2 py-1 underline dark:border-stone-500" rel="prev" href={page.url.prev} > Previous page </a> ) } { [...Array(totalPages).keys()] .map((p) => p + 1) .map((p) => p === page.currentPage ? ( <span class="border-r border-stone-400 px-4 py-1 font-semibold text-stone-900 dark:border-stone-500 dark:text-stone-50"> {p} </span> ) : ( <a class="text-link border-r border-stone-400 px-2 py-1 underline dark:border-stone-500" href={page.url.current.replace(`/stories/${page.currentPage}`, `/stories/${p}`)} > {p} </a> ), ) } { page.url.next && ( <a class="text-link px-2 py-1 underline" rel="next" href={page.url.next}> Next page </a> ) } </div> </GalleryLayout>