Allow articles to have many authors with links

This commit is contained in:
Kalle
2025-01-03 21:08:43 +02:00
parent 2cd597912b
commit 0f9e620991
5 changed files with 59 additions and 19 deletions

View File

@@ -1,7 +1,14 @@
import { z } from "zod";
const authorName = z.string().min(1);
const author = z.union([
authorName,
z.object({ name: authorName, link: z.string().url() }),
]);
export const articleDataSchema = z.object({
title: z.string().min(1),
author: z.string().min(1),
author: z.union([author, z.array(author)]),
date: z.date(),
});

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import { ZodError } from "zod";
import { ZodError, type z } from "zod";
import { ARTICLES_FOLDER_PATH } from "../articles-constants";
import { articleDataSchema } from "../articles-schemas.server";
@@ -23,8 +23,8 @@ export function articleBySlug(slug: string) {
month: "long",
year: "numeric",
}),
authorLink: authorToLink(restParsed.author),
...restParsed,
authors: normalizeAuthors(restParsed.author),
title: restParsed.title,
};
} catch (e) {
if (!(e instanceof Error)) throw e;
@@ -37,8 +37,20 @@ export function articleBySlug(slug: string) {
}
}
function authorToLink(author: string) {
if (author === "Riczi") return "https://twitter.com/Riczi2k";
export function normalizeAuthors(
authors: z.infer<typeof articleDataSchema>["author"],
): Array<{ name: string; link: string | null }> {
if (Array.isArray(authors)) {
return authors.map((author) => {
if (typeof author === "string") {
return { name: author, link: null };
}
return author;
});
}
return;
if (typeof authors === "string") {
return [{ name: authors, link: null }];
}
return [authors];
}

View File

@@ -1,15 +1,15 @@
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import type { z } from "zod";
import { ARTICLES_FOLDER_PATH } from "../articles-constants";
import { articleDataSchema } from "../articles-schemas.server";
import { type articleBySlug, normalizeAuthors } from "./bySlug.server";
export async function mostRecentArticles(count: number) {
const files = await fs.promises.readdir(ARTICLES_FOLDER_PATH);
const articles: Array<
z.infer<typeof articleDataSchema> & {
Omit<NonNullable<ReturnType<typeof articleBySlug>>, "content"> & {
slug: string;
dateString: string;
}
@@ -21,15 +21,17 @@ export async function mostRecentArticles(count: number) {
);
const { data } = matter(rawMarkdown);
const articleData = articleDataSchema.parse(data);
const { date, ...restParsed } = articleDataSchema.parse(data);
articles.push({
...articleData,
date,
slug: file.replace(".md", ""),
dateString: articleData.date.toLocaleDateString("en-US", {
dateString: date.toLocaleDateString("en-US", {
day: "2-digit",
month: "long",
year: "numeric",
}),
authors: normalizeAuthors(restParsed.author),
title: restParsed.title,
});
}

View File

@@ -3,7 +3,7 @@ import type {
MetaFunction,
SerializeFrom,
} from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Link, useLoaderData } from "@remix-run/react";
import Markdown from "markdown-to-jsx";
import * as React from "react";
import { Main } from "~/components/Main";
@@ -88,9 +88,22 @@ export default function ArticlePage() {
function Author() {
const data = useLoaderData<typeof loader>();
if (data.authorLink) {
return <a href={data.authorLink}>{data.author}</a>;
}
return data.authors.map((author, i) => {
if (!author.link) return author.name;
return <>{data.author}</>;
const authorLink = author.link.includes("https://sendou.ink")
? author.link.replace("https://sendou.ink", "")
: author.link;
return (
<React.Fragment key={author.name}>
{author.link.includes("https://sendou.ink") ? (
<Link to={authorLink}>{author.name}</Link>
) : (
<a href={author.link}>{author.name}</a>
)}
{i < data.authors.length - 1 ? " & " : ""}
</React.Fragment>
);
});
}

View File

@@ -6,6 +6,7 @@ import { ARTICLES_MAIN_PAGE, articlePage, navIconUrl } from "~/utils/urls";
import { mostRecentArticles } from "../core/list.server";
import "~/styles/front.css";
import { joinListToNaturalString } from "../../../utils/arrays";
const MAX_ARTICLES_COUNT = 100;
@@ -39,8 +40,13 @@ export default function ArticlesMainPage() {
{article.title}
</Link>
<div className="text-xs text-lighter">
{t("common:articles.by", { author: article.author })} •{" "}
<time>{article.dateString}</time>
{t("common:articles.by", {
author: joinListToNaturalString(
article.authors.map((a) => a.name),
"&",
),
})}{" "}
• <time>{article.dateString}</time>
</div>
</li>
))}