gallery.badmanners.xyz/src/pages/stories/[...page].astro

179 lines
6.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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";
import { IconSquareRSS } from "@components/icons";
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 grow 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="my-4 flex">
<p class="p-summary grow">The bulk of my content!</p>
<a class="u-url text-link ml-2 mr-10 p-2" href="/stories/feed.xml" rel="alternate" title="RSS feed" data-tooltip>
<IconSquareRSS width="2rem" height="2rem" />
<span class="sr-only">RSS feed</span>
</a>
</div>
<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 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={p === 1 ? "/stories/" : `/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={p === 1 ? "/stories/" : `/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>