blog/routes/index.tsx

119 lines
3.1 KiB
TypeScript

import { Handlers, PageProps } from "$fresh/server.ts";
import { listPosts, Post } from "@/libs/post/post.ts";
import { renderMarkdown } from "@/libs/post/markdown.ts";
import { format } from "$std/datetime/format.ts";
import {
EmojiHandWiving,
IconFresh,
IconGitea,
IconWoodpecker,
} from "@/components/icons.tsx";
function Hero() {
return (
<div
class={`px-4 py-8 mx-auto bg-[#a9def9] -top-20 -mb-20 pt-24 relative`}
>
<div class="max-w-screen-md mx-auto">
<div class="w-full h-full flex flex-col items-center justify-center">
<h1 class="text-4xl font-bold">
Welcome to my blog! <EmojiHandWiving class="h-14 w-14" />
</h1>
<p class="my-4 text-gray-500 text-lg text-center">
This is a simple blog built with{" "}
<a href="https://fresh.deno.dev/">
<IconFresh class="inline align-top" />
</a>{" "}
on{" "}
<a href="https://gitea.aireone.xyz">
<IconGitea class="inline align-top mt-0.5" />
</a>{" "}
and deployed using{" "}
<a href="https://woodpecker.aireone.xyz">
<IconWoodpecker class="inline align-top text-black" />
</a>.
</p>
</div>
</div>
</div>
);
}
function PostItem(
{ slug, title, publishedAt, html }:
& Pick<Post, "slug" | "title" | "publishedAt">
& {
html: string; // this should be the post.markdown rendered as HTML
},
) {
return (
<div class="min-w-0 w-full md:max-w-[80ch]">
<div class="flex items-center justify-between">
<span class="group">
<a // href={`/blog/${slug}`}
class="text-4xl text-gray-900 tracking-tight font-bold md:mt-0 px-4 md:px-0 mb-4">
{title}
</a>
<a
id={slug}
class="invisible group-hover:visible ml-1 text-[#0969da] text-4xl font-bold"
tabindex={-1}
href={`#${slug}`}
>
<span aria-hidden="true">#</span>
</a>
</span>
<span class="text-gray-500">
{format(publishedAt, "yyyy-MM-dd")}
</span>
</div>
<div
class="markdown-body mb-8"
dangerouslySetInnerHTML={{
__html: html,
}}
/>
</div>
);
}
interface Props {
posts: Post[];
}
export const handler: Handlers<Props> = {
async GET(_, ctx) {
const posts = await listPosts();
return ctx.render({
posts,
});
},
};
export default function Home({ data: { posts } }: PageProps<Props>) {
return (
<>
<Hero />
<div class="max-w-screen-md mx-auto py-8">
<h2 class="text-2xl font-bold">Posts</h2>
{posts.map(({ slug, title, publishedAt, markdown }, index) => (
<>
<PostItem
key={slug}
slug={slug}
title={title}
publishedAt={publishedAt}
html={renderMarkdown(markdown).html}
/>
{index < posts.length - 1 && (
<hr class="my-8 border-t border-gray-200" />
)}
</>
))}
</div>
</>
);
}