Better microformats support and add PUBLISH_DRAFTS envvar
This commit is contained in:
parent
132b2b69f3
commit
a335aff2d3
31 changed files with 269 additions and 153 deletions
|
|
@ -47,8 +47,8 @@ export const GET: APIRoute<Props, Params> = async ({ props: { story }, site }) =
|
|||
const { lang } = story.data;
|
||||
const copyrightedCharacters = await formatCopyrightedCharacters(story.data.copyrightedCharacters);
|
||||
const authorsList = await getEntries(story.data.authors);
|
||||
const commissionersList = story.data.commissioner && (await getEntries(story.data.commissioner));
|
||||
const requestersList = story.data.requester && (await getEntries(story.data.requester));
|
||||
const commissionersList = story.data.commissioners && (await getEntries(story.data.commissioners));
|
||||
const requestersList = story.data.requesters && (await getEntries(story.data.requesters));
|
||||
|
||||
const description = await Promise.all(
|
||||
WEBSITE_LIST.map(async ({ website, exportFormat }) => {
|
||||
|
|
|
|||
|
|
@ -52,18 +52,18 @@ async function storyFeedItem(
|
|||
"story/authors",
|
||||
(await getEntries(data.authors)).map((author) => getLinkForUser(author, data.lang)),
|
||||
)}</p>` +
|
||||
(data.requester
|
||||
(data.requesters
|
||||
? `<p>${t(
|
||||
data.lang,
|
||||
"story/requested_by",
|
||||
(await getEntries(data.requester)).map((requester) => getLinkForUser(requester, data.lang)),
|
||||
(await getEntries(data.requesters)).map((requester) => getLinkForUser(requester, data.lang)),
|
||||
)}</p>`
|
||||
: "") +
|
||||
(data.commissioner
|
||||
(data.commissioners
|
||||
? `<p>${t(
|
||||
data.lang,
|
||||
"story/commissioned_by",
|
||||
(await getEntries(data.commissioner)).map((commissioner) => getLinkForUser(commissioner, data.lang)),
|
||||
(await getEntries(data.commissioners)).map((commissioner) => getLinkForUser(commissioner, data.lang)),
|
||||
)}</p>`
|
||||
: "") +
|
||||
`<hr><p><em>${t(data.lang, "story/warnings", data.wordCount, data.contentWarning)}</em></p>` +
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
---
|
||||
import { Image } from "astro:assets";
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
import { getCollection, getEntries, type CollectionEntry } from "astro:content";
|
||||
import GalleryLayout from "../layouts/GalleryLayout.astro";
|
||||
import { t } from "../i18n";
|
||||
import { DEFAULT_LANG, t } from "../i18n";
|
||||
import UserComponent from "../components/UserComponent.astro";
|
||||
|
||||
type GameWithPubDate = CollectionEntry<"games"> & { data: { pubDate: Date } };
|
||||
|
||||
const games = (
|
||||
(await getCollection("games", (game) => !game.data.isDraft && game.data.pubDate)) as GameWithPubDate[]
|
||||
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
|
||||
const games = await Promise.all(
|
||||
((await getCollection("games", (game) => !game.data.isDraft && game.data.pubDate)) as GameWithPubDate[])
|
||||
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
|
||||
.map(async (game) => ({
|
||||
...game,
|
||||
authors: await getEntries(game.data.authors),
|
||||
})),
|
||||
);
|
||||
---
|
||||
|
||||
<GalleryLayout pageTitle="Games" class="h-feed">
|
||||
|
|
@ -17,7 +23,7 @@ const games = (
|
|||
<p class="p-summary my-4">A game that I've gone and done.</p>
|
||||
<ul class="my-6 flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
|
||||
{
|
||||
games.map((game) => (
|
||||
games.map((game, i) => (
|
||||
<li class="h-entry">
|
||||
<a
|
||||
class="u-url text-link hover:underline focus:underline"
|
||||
|
|
@ -26,7 +32,13 @@ const games = (
|
|||
>
|
||||
{game.data.thumbnail ? (
|
||||
<div class="flex aspect-[630/500] max-w-[288px] justify-center">
|
||||
<Image class="u-photo m-auto" src={game.data.thumbnail} alt={`Thumbnail for ${game.data.title}`} width={288} />
|
||||
<Image
|
||||
loading={i < 10 ? "eager" : "lazy"}
|
||||
class="u-photo m-auto"
|
||||
src={game.data.thumbnail}
|
||||
alt={`Thumbnail for ${game.data.title}`}
|
||||
width={288}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div class="max-w-[288px] text-sm">
|
||||
|
|
@ -37,6 +49,11 @@ const games = (
|
|||
</time>
|
||||
</div>
|
||||
</a>
|
||||
<div style={{ display: "none" }}>
|
||||
{game.authors.map((author) => (
|
||||
<UserComponent rel="author" class="p-author" user={author} lang={DEFAULT_LANG} />
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import type { GetStaticPaths } from "astro";
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import GameLayout from "../../layouts/GameLayout.astro";
|
||||
import { PUBLISH_DRAFTS } from "astro:env/server";
|
||||
|
||||
type Props = CollectionEntry<"games">;
|
||||
|
||||
|
|
@ -11,10 +12,12 @@ type Params = {
|
|||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const games = await getCollection("games");
|
||||
return games.map((game) => ({
|
||||
params: { slug: game.slug } satisfies Params,
|
||||
props: game satisfies Props,
|
||||
}));
|
||||
return games
|
||||
.filter((game) => import.meta.env.DEV || PUBLISH_DRAFTS || !game.data.isDraft)
|
||||
.map((game) => ({
|
||||
params: { slug: game.slug } satisfies Params,
|
||||
props: game satisfies Props,
|
||||
}));
|
||||
};
|
||||
|
||||
const game = Astro.props;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
---
|
||||
import type { ImageMetadata } from "astro";
|
||||
import { type CollectionEntry, type CollectionKey, getCollection } from "astro:content";
|
||||
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
|
||||
import { Image } from "astro:assets";
|
||||
import GalleryLayout from "../layouts/GalleryLayout.astro";
|
||||
import { t } from "../i18n";
|
||||
import { DEFAULT_LANG, t, type Lang } from "../i18n";
|
||||
import UserComponent from "../components/UserComponent.astro";
|
||||
|
||||
const MAX_ITEMS = 8;
|
||||
const MAX_ITEMS = 10;
|
||||
|
||||
interface LatestItemsEntry {
|
||||
type: string;
|
||||
thumbnail?: ImageMetadata;
|
||||
href: string;
|
||||
title: string;
|
||||
lang: Lang;
|
||||
authors: CollectionEntry<"users">[];
|
||||
altText: string;
|
||||
pubDate: Date;
|
||||
}
|
||||
|
|
@ -32,27 +35,42 @@ const games = (
|
|||
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
|
||||
.slice(0, MAX_ITEMS);
|
||||
|
||||
const latestItems: LatestItemsEntry[] = [
|
||||
stories.map<LatestItemsEntry>((story) => ({
|
||||
type: "Story",
|
||||
thumbnail: story.data.thumbnail,
|
||||
href: `/stories/${story.slug}`,
|
||||
title: story.data.title,
|
||||
altText: t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning),
|
||||
pubDate: story.data.pubDate,
|
||||
})),
|
||||
games.map<LatestItemsEntry>((game) => ({
|
||||
type: "Game",
|
||||
thumbnail: game.data.thumbnail,
|
||||
href: `/games/${game.slug}`,
|
||||
title: game.data.title,
|
||||
altText: t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning),
|
||||
pubDate: game.data.pubDate,
|
||||
})),
|
||||
]
|
||||
.flat()
|
||||
.sort((a, b) => b.pubDate.getTime() - a.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: DEFAULT_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">
|
||||
|
|
@ -60,8 +78,8 @@ const latestItems: LatestItemsEntry[] = [
|
|||
<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.
|
||||
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>
|
||||
|
|
@ -85,7 +103,13 @@ const latestItems: LatestItemsEntry[] = [
|
|||
<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 class="u-photo m-auto" src={entry.thumbnail} alt={`Thumbnail for ${entry.title}`} width={192} />
|
||||
<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">
|
||||
|
|
@ -93,10 +117,17 @@ const latestItems: LatestItemsEntry[] = [
|
|||
<br />
|
||||
<span class="italic">
|
||||
<span class="p-category">{entry.type}</span> –{" "}
|
||||
<time class="dt-published" datetime={entry.pubDate.toISOString().slice(0, 10)}>{entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}</time>
|
||||
<time class="dt-published" datetime={entry.pubDate.toISOString().slice(0, 10)}>
|
||||
{entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
|
||||
</time>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<div style={{ display: "none" }}>
|
||||
{entry.authors.map((author) => (
|
||||
<UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { GetStaticPaths } from "astro";
|
|||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import getReadingTime from "reading-time";
|
||||
import StoryLayout from "../../layouts/StoryLayout.astro";
|
||||
import { PUBLISH_DRAFTS } from "astro:env/server";
|
||||
|
||||
type Props = CollectionEntry<"stories">;
|
||||
|
||||
|
|
@ -12,10 +13,12 @@ type Params = {
|
|||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const stories = await getCollection("stories");
|
||||
return stories.map((story) => ({
|
||||
params: { slug: story.slug } satisfies Params,
|
||||
props: story satisfies Props,
|
||||
}));
|
||||
return stories
|
||||
.filter((story) => import.meta.env.DEV || PUBLISH_DRAFTS || !story.data.isDraft)
|
||||
.map((story) => ({
|
||||
params: { slug: story.slug } satisfies Params,
|
||||
props: story satisfies Props,
|
||||
}));
|
||||
};
|
||||
|
||||
const story = Astro.props;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
---
|
||||
import type { GetStaticPaths, Page } from "astro";
|
||||
import { Image } from "astro:assets";
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
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>;
|
||||
page: Page<StoryWithPubDate & { authors: CollectionEntry<"users">[] }>;
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async ({ paginate }) => {
|
||||
const stories = (
|
||||
(await getCollection("stories", (story) => !story.data.isDraft && story.data.pubDate)) as StoryWithPubDate[]
|
||||
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
|
||||
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 }[];
|
||||
};
|
||||
|
||||
|
|
@ -44,20 +50,22 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
)
|
||||
}
|
||||
{
|
||||
[...Array(totalPages).keys()].map((p) =>
|
||||
p + 1 == 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 + 1}
|
||||
</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 + 1}`)}
|
||||
>
|
||||
{p + 1}
|
||||
</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 && (
|
||||
|
|
@ -69,7 +77,7 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
</div>
|
||||
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
|
||||
{
|
||||
page.data.map((story) => (
|
||||
page.data.map((story, i) => (
|
||||
<li class="h-entry break-inside-avoid">
|
||||
<a
|
||||
class="u-url text-link hover:underline focus:underline"
|
||||
|
|
@ -79,6 +87,7 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
{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}`}
|
||||
|
|
@ -94,6 +103,11 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
</time>
|
||||
</div>
|
||||
</a>
|
||||
<div style={{ display: "none" }}>
|
||||
{story.authors.map((author) => (
|
||||
<UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} />
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
|
@ -107,20 +121,22 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
)
|
||||
}
|
||||
{
|
||||
[...Array(totalPages).keys()].map((p) =>
|
||||
p + 1 == 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 + 1}
|
||||
</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 + 1}`)}
|
||||
>
|
||||
{p + 1}
|
||||
</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 && (
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summ
|
|||
content="The Lost of the Marshes || The story of Quince, Nikili, and Suu."
|
||||
/>
|
||||
<h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">{series.data.name}</h1>
|
||||
<p class="p-summary my-4">This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content.</p>
|
||||
<p class="p-summary my-4">
|
||||
This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content.
|
||||
</p>
|
||||
<section class="my-2" aria-labelledby="main-chapters">
|
||||
<h2
|
||||
id="main-chapters"
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ const totalWorksWithTag = t(
|
|||
day: "numeric",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</time>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue