sendou.ink/app/routes/a.$slug.tsx
Kalle 34ca290bdd
Redesign (#1179)
* Remove light mode

* Trim header

* New front page initial

* Get rid of build layout

* Breadcrumbs

* Desktop side nav

* Overhaul colors

* Add breadcrumbs

* New sub nav style

* Front page action buttons

* Add back add new build button

* Add articles page with icon

* Minor Object damage page layout tweaks

* Remove one unnecessary render from object damage

* Fix wrong link in article page

* Profile -> My Page in header

* Log in/out buttons in front

* Add drawings to front page

* Remove unnecessary comment
2022-12-05 16:05:51 +02:00

95 lines
2.3 KiB
TypeScript

import Markdown from "markdown-to-jsx";
import { Main } from "~/components/Main";
import {
json,
type SerializeFrom,
type LoaderArgs,
type MetaFunction,
} from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import * as React from "react";
import { articleBySlug } from "~/modules/articles";
import invariant from "tiny-invariant";
import { makeTitle } from "~/utils/strings";
import {
articlePage,
articlePreviewUrl,
ARTICLES_MAIN_PAGE,
navIconUrl,
} from "~/utils/urls";
import type { SendouRouteHandle } from "~/utils/remix";
import { notFoundIfFalsy } from "~/utils/remix";
export const handle: SendouRouteHandle = {
breadcrumb: ({ match }) => {
const data = match.data as SerializeFrom<typeof loader>;
return [
{
imgPath: navIconUrl("articles"),
href: ARTICLES_MAIN_PAGE,
type: "IMAGE",
},
{
text: data.title,
href: articlePage(data.slug),
type: "TEXT",
},
];
},
};
export const meta: MetaFunction = (args) => {
invariant(args.params["slug"]);
const data = args.data as SerializeFrom<typeof loader> | null;
if (!data) return {};
const description = data.content.trim().split("\n")[0];
return {
title: makeTitle(data.title),
"og:title": data.title,
description,
"og:description": description,
"twitter:card": "summary_large_image",
"og:image": articlePreviewUrl(args.params["slug"]),
"og:type": "article",
"og:site_name": "sendou.ink",
};
};
export const loader = ({ params }: LoaderArgs) => {
invariant(params["slug"]);
const article = notFoundIfFalsy(articleBySlug(params["slug"]));
return json({ ...article, slug: params["slug"] });
};
export default function ArticlePage() {
const data = useLoaderData<typeof loader>();
return (
<Main>
<article className="article">
<h1>{data.title}</h1>
<div className="text-sm text-lighter">
by <Author /> <time>{data.dateString}</time>
</div>
<Markdown options={{ wrapper: React.Fragment }}>
{data.content}
</Markdown>
</article>
</Main>
);
}
function Author() {
const data = useLoaderData<typeof loader>();
if (data.authorLink) {
return <a href={data.authorLink}>{data.author}</a>;
}
return <>{data.author}</>;
}