Improve feed items
This commit is contained in:
parent
e859109ade
commit
568b7709ec
5 changed files with 74 additions and 44 deletions
src
|
@ -16,10 +16,6 @@ export const UI_STRINGS: Record<string, TranslationRecord> = {
|
||||||
: `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`,
|
: `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`,
|
||||||
tok: (names: string[]) => names.join(" en "),
|
tok: (names: string[]) => names.join(" en "),
|
||||||
},
|
},
|
||||||
"export_story/warnings": {
|
|
||||||
eng: (wordCount: number | string, contentWarning: string) => `*Word count: ${wordCount}. ${contentWarning}*`,
|
|
||||||
tok: (_wordCount: number | string, contentWarning: string) => `*${contentWarning}*`,
|
|
||||||
},
|
|
||||||
"export_story/writing": {
|
"export_story/writing": {
|
||||||
eng: (authorsList: string[]) => `Writing: ${authorsList.join(" ")}`,
|
eng: (authorsList: string[]) => `Writing: ${authorsList.join(" ")}`,
|
||||||
tok: (authorsList: string[]) => `lipu ni li tan jan ni: ${authorsList.join(" en ")}`,
|
tok: (authorsList: string[]) => `lipu ni li tan jan ni: ${authorsList.join(" en ")}`,
|
||||||
|
@ -45,9 +41,9 @@ export const UI_STRINGS: Record<string, TranslationRecord> = {
|
||||||
eng: "Toggle dark mode",
|
eng: "Toggle dark mode",
|
||||||
tok: "o ante e kule lipu",
|
tok: "o ante e kule lipu",
|
||||||
},
|
},
|
||||||
"story/word_count": {
|
"story/warnings": {
|
||||||
eng: (wordCount: string | number) => `Word count: ${wordCount}.`,
|
eng: (wordCount: number | string, contentWarning: string) => `Word count: ${wordCount}. ${contentWarning}`,
|
||||||
tok: "",
|
tok: (_wordCount: number | string, contentWarning: string) => `${contentWarning}`,
|
||||||
},
|
},
|
||||||
"story/publish_date": {
|
"story/publish_date": {
|
||||||
eng: (date: string) => date,
|
eng: (date: string) => date,
|
||||||
|
@ -99,7 +95,7 @@ export const UI_STRINGS: Record<string, TranslationRecord> = {
|
||||||
eng: (owner: string, charactersList: string[]) =>
|
eng: (owner: string, charactersList: string[]) =>
|
||||||
charactersList.length == 1
|
charactersList.length == 1
|
||||||
? `${charactersList[0]} is © ${owner}`
|
? `${charactersList[0]} is © ${owner}`
|
||||||
: `${(UI_STRINGS["util/join_names"]!["eng"] as (arg: string[]) => string)(charactersList)} are © ${owner}`,
|
: `${(UI_STRINGS["util/join_names"]!.eng as (arg: string[]) => string)(charactersList)} are © ${owner}`,
|
||||||
},
|
},
|
||||||
"characters/all_characters_are_copyrighted_by": {
|
"characters/all_characters_are_copyrighted_by": {
|
||||||
eng: (owner: string) => `All characters are © ${owner}`,
|
eng: (owner: string) => `All characters are © ${owner}`,
|
||||||
|
|
|
@ -199,8 +199,7 @@ const thumbnail =
|
||||||
}
|
}
|
||||||
<div id="content-warning">
|
<div id="content-warning">
|
||||||
<p>
|
<p>
|
||||||
{t(props.lang, "story/word_count", props.wordCount)}
|
{t(props.lang, "story/warnings", props.wordCount, props.contentWarning.trim())}
|
||||||
{props.contentWarning}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -235,7 +235,7 @@ export const GET: APIRoute<Props, Params> = async ({ props: { story }, site }) =
|
||||||
const storyDescription = (
|
const storyDescription = (
|
||||||
[
|
[
|
||||||
story.data.description,
|
story.data.description,
|
||||||
t(lang, "export_story/warnings", story.data.wordCount, story.data.contentWarning.trim()),
|
`*${t(lang, "story/warnings", story.data.wordCount, story.data.contentWarning.trim())}*`,
|
||||||
t(
|
t(
|
||||||
lang,
|
lang,
|
||||||
"export_story/writing",
|
"export_story/writing",
|
||||||
|
|
|
@ -23,10 +23,10 @@ const getLinkForUser = (user: CollectionEntry<"users">, lang: Lang) => {
|
||||||
const userName = user.data.nameLang[lang] || user.data.name;
|
const userName = user.data.nameLang[lang] || user.data.name;
|
||||||
if (user.data.preferredLink) {
|
if (user.data.preferredLink) {
|
||||||
const link = user.data.links[user.data.preferredLink]!;
|
const link = user.data.links[user.data.preferredLink]!;
|
||||||
return `<a href="${typeof link === "string" ? link : link[0]}">${userName}</a>`
|
return `<a href="${typeof link === "string" ? link : link[0]}">${userName}</a>`;
|
||||||
}
|
}
|
||||||
return userName;
|
return userName;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const GET: APIRoute = async ({ site }) => {
|
export const GET: APIRoute = async ({ site }) => {
|
||||||
const stories = (await getCollection("stories", (story) => !story.data.isDraft))
|
const stories = (await getCollection("stories", (story) => !story.data.isDraft))
|
||||||
|
@ -42,37 +42,71 @@ export const GET: APIRoute = async ({ site }) => {
|
||||||
description: "Stories, games, and (possibly) more by Bad Manners",
|
description: "Stories, games, and (possibly) more by Bad Manners",
|
||||||
site: site as URL,
|
site: site as URL,
|
||||||
items: [
|
items: [
|
||||||
await Promise.all(stories.map<Promise<FeedItem>>(async ({ data, slug, body }) => ({
|
await Promise.all(
|
||||||
title: `New story! "${data.title}"`,
|
stories.map<Promise<FeedItem>>(async ({ data, slug, body }) => ({
|
||||||
pubDate: toNoonUTCDate(data.pubDate),
|
title: `New story! "${data.title}"`,
|
||||||
link: `/stories/${slug}`,
|
pubDate: toNoonUTCDate(data.pubDate),
|
||||||
description:
|
link: `/stories/${slug}`,
|
||||||
`Word count: ${data.wordCount}. ${data.contentWarning} ${data.descriptionPlaintext || data.description}`
|
description:
|
||||||
|
`${t(data.lang, "story/warnings", data.wordCount, data.contentWarning.trim())} ${data.descriptionPlaintext || data.description}`
|
||||||
|
.replaceAll(/[\n ]+/g, " ")
|
||||||
|
.trim(),
|
||||||
|
categories: ["story"],
|
||||||
|
content: sanitizeHtml(
|
||||||
|
`<h1>${data.title}</h1>` +
|
||||||
|
`<p>${t(
|
||||||
|
data.lang,
|
||||||
|
"story/authors",
|
||||||
|
[data.authors].flatMap((authorArray) => {
|
||||||
|
if (!Array.isArray(authorArray)) {
|
||||||
|
authorArray = [authorArray];
|
||||||
|
}
|
||||||
|
return authorArray.map((author) =>
|
||||||
|
getLinkForUser(users.find((user) => user.id === author.id)!, data.lang),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
)}</p>` +
|
||||||
|
(data.requester
|
||||||
|
? `<p>${t(data.lang, "export_story/request_for", getLinkForUser(users.find((user) => user.id === data.requester!.id)!, data.lang))}</p>`
|
||||||
|
: "") +
|
||||||
|
(data.commissioner
|
||||||
|
? `<p>${t(data.lang, "export_story/commissioned_by", getLinkForUser(users.find((user) => user.id === data.commissioner!.id)!, data.lang))}</p>`
|
||||||
|
: "") +
|
||||||
|
`<hr><p><em>${t(data.lang, "story/warnings", data.wordCount, data.contentWarning.trim())}</em></p>` +
|
||||||
|
`<hr>${tinyDecode(await marked(body))}` +
|
||||||
|
`<hr>${tinyDecode(await marked(data.description))}`,
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
await Promise.all(
|
||||||
|
games.map<Promise<FeedItem>>(async ({ data, slug, body }) => ({
|
||||||
|
title: `New game! "${data.title}"`,
|
||||||
|
pubDate: toNoonUTCDate(data.pubDate),
|
||||||
|
link: `/games/${slug}`,
|
||||||
|
description: `${data.contentWarning} ${data.descriptionPlaintext || data.description}`
|
||||||
.replaceAll(/[\n ]+/g, " ")
|
.replaceAll(/[\n ]+/g, " ")
|
||||||
.trim(),
|
.trim(),
|
||||||
categories: ["story"],
|
categories: ["game"],
|
||||||
content: sanitizeHtml(`<h1>${data.title}</h1><p>${t(data.lang, "story/authors", [data.authors].flatMap((authorArray => {
|
content: sanitizeHtml(
|
||||||
if (!Array.isArray(authorArray)) {
|
`<h1>${data.title}</h1>` +
|
||||||
authorArray = [authorArray]
|
`<p>${t(
|
||||||
}
|
data.lang,
|
||||||
return authorArray.map(author => getLinkForUser(users.find(user => user.id === author.id)!, data.lang));
|
"story/authors",
|
||||||
})))}</p>${data.requester ? `<p>${t(data.lang, "export_story/request_for", getLinkForUser(users.find(user => user.id === data.requester!.id)!, data.lang))}</p>` : ""}${data.commissioner ? `<p>${t(data.lang, "export_story/commissioned_by", getLinkForUser(users.find(user => user.id === data.commissioner!.id)!, data.lang))}</p>` : ""}<hr>${tinyDecode(await marked(body))}`),
|
[data.authors].flatMap((authorArray) => {
|
||||||
}))),
|
if (!Array.isArray(authorArray)) {
|
||||||
await Promise.all(games.map<Promise<FeedItem>>(async ({ data, slug, body }) => ({
|
authorArray = [authorArray];
|
||||||
title: `New game! "${data.title}"`,
|
}
|
||||||
pubDate: toNoonUTCDate(data.pubDate),
|
return authorArray.map((author) =>
|
||||||
link: `/games/${slug}`,
|
getLinkForUser(users.find((user) => user.id === author.id)!, data.lang),
|
||||||
description: `${data.contentWarning} ${data.descriptionPlaintext || data.description}`
|
);
|
||||||
.replaceAll(/[\n ]+/g, " ")
|
}),
|
||||||
.trim(),
|
)}</p>` +
|
||||||
categories: ["game"],
|
`<hr><p><em>${data.contentWarning.trim()}</em></p>` +
|
||||||
content: sanitizeHtml(`<h1>${data.title}</h1><p>${t(data.lang, "story/authors", [data.authors].flatMap((authorArray => {
|
`<hr>${tinyDecode(await marked(body))}` +
|
||||||
if (!Array.isArray(authorArray)) {
|
`<hr>${tinyDecode(await marked(data.description))}`,
|
||||||
authorArray = [authorArray]
|
),
|
||||||
}
|
})),
|
||||||
return authorArray.map(author => getLinkForUser(users.find(user => user.id === author.id)!, data.lang));
|
),
|
||||||
})))}</p><hr>${tinyDecode(await marked(body))}`),
|
|
||||||
}))),
|
|
||||||
]
|
]
|
||||||
.flat()
|
.flat()
|
||||||
.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime())
|
.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime())
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import type { APIRoute } from "astro";
|
import type { APIRoute } from "astro";
|
||||||
|
|
||||||
const licenses = `The briefcase logo and any unattributed characters are copyrighted and trademarked by me.
|
const licenses = `
|
||||||
|
The briefcase logo and any unattributed characters are copyrighted and trademarked by me.
|
||||||
|
|
||||||
The Noto Sans and Noto Serif typefaces are copyrighted to the Noto Project Authors and distributed under the SIL Open Font License v1.1.
|
The Noto Sans and Noto Serif typefaces are copyrighted to the Noto Project Authors and distributed under the SIL Open Font License v1.1.
|
||||||
|
|
||||||
The generic SVG icons were created by Font Awesome and are distributed under the CC BY 4.0 license.
|
The generic SVG icons were created by Font Awesome and are distributed under the CC BY 4.0 license.
|
||||||
|
|
||||||
All third-party trademarks belong to their respective owners, and I'm not affiliated with any of them.
|
All third-party trademarks belong to their respective owners, and I'm not affiliated with any of them.
|
||||||
`;
|
`.trim();
|
||||||
|
|
||||||
export const GET: APIRoute = () => {
|
export const GET: APIRoute = () => {
|
||||||
return new Response(licenses, {
|
return new Response(licenses, {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue