Files
site-profile/src/pages/blog/[...slug].astro

185 lines
5.7 KiB
Plaintext

---
import { type CollectionEntry, getCollection } from 'astro:content';
import getReadingTime from 'reading-time';
import { marked } from 'marked';
import markedShiki from 'marked-shiki';
import { createHighlighter } from 'shiki';
import { readItems, readSingleton } from '@directus/sdk';
import Image from '@components/ui/images/Image.astro';
import SocialShareButton from '@components/buttons/SocialShareButton.astro';
import BaseLayout from '@layouts/BaseLayout.astro';
import directus from '@lib/directus';
import { getDirectusImageURL } from '@lib/directusFunctions';
import { formatDate } from '@support/time';
const post = Astro.props;
export async function getStaticPaths() {
const posts = await directus.request(readItems('posts'));
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
const global = await directus.request(readSingleton('site_global'));
const category: CollectionEntry<'categories'> = (await getCollection('categories'))
.filter((c) => c.slug === post.category)
.pop() as CollectionEntry<'categories'>;
const readingTime = getReadingTime(post.content);
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: ['typescript', 'python', 'css', 'html', 'yaml', 'bash', 'json'],
});
marked.use(markedShiki({
highlight(code, lang) {
return highlighter.codeToHtml(code, {
lang: lang || 'plaintext',
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: false,
});
}
}));
const content = marked.parse(post.content);
---
<BaseLayout
title={post.title}
description={post.description}
ogImage={getDirectusImageURL(post.image)}
structuredData={{
'@context': 'https://schema.org',
'@type': 'NewsArticle',
inLanguage: 'en-US',
'@id': Astro.url.href,
url: Astro.url.href,
description: post.description,
isPartOf: {
'@type': 'WebSite',
url: `${global.site_url}/blog`,
name: global.name,
description: global.about,
},
image: [],
headline: post.title,
datePublished: post.published_date,
dateModified: post.updated_date,
author: [
{
'@type': 'Person',
name: `${global.name}`,
url: `${global.site_url}`,
},
],
}}
>
<section class="max-w-6xl px-4 sm:px-6 lg:px-8 pt-8 lg:pt-12 pb-12 mx-auto">
<div class="smooth-reveal relative w-full">
<div class="sm:shadow-xs sm:dark:shadow-md rounded-2xl mt-4 sm:mt-0">
<Image
class="rounded-2xl sm:rounded-b-none w-full max-h-150 object-cover"
src={getDirectusImageURL(post.image)}
alt={post.image_alt}
draggable="false"
format="webp"
loading="lazy"
inferSize={true}
/>
<div class="sm:bg-background-card rounded-b-2xl px-0 sm:px-6 md:px-10 lg:px-14 py-6">
<div class="text-center sm:text-left mt-4">
<h2 class="card-text-header block">
{post.title}
</h2>
<ol class="flex items-center justify-center sm:justify-start whitespace-nowrap gap-2 sm:gap-0 mt-6 sm:mt-4">
<li class="inline-flex items-center">
<a
class="inline-flex items-center text-secondary hover:text-secondary-hover text-sm transition-all duration-300"
href=`/categories/${category.slug}`
data-astro-prefetch
>
{category?.data?.title}
</a>
<span class="shrink-0 text-secondary text-sm mx-2 sm:mx-4">
/
</span>
</li>
<li class="inline-flex items-center">
<span class="shrink-0 text-secondary text-sm">
{formatDate(post.published_date)}
</span>
<span class="shrink-0 text-secondary text-sm mx-2 sm:mx-4">
/
</span>
</li>
<li class="inline-flex items-center">
<span class="shrink-0 text-secondary text-sm">
{readingTime.minutes.toPrecision(1)} minutes to read
</span>
</li>
</ol>
</div>
<div class="border-t border-divider mt-10 mb-10"/>
<article class="text-header prose prose-blog sm:prose-lg dark:prose-invert max-w-none">
<div set:html={content} />
</article>
<div class="grid sm:flex sm:items-center sm:justify-between gap-y-5 sm:gap-y-0 max-w-5xl mx-auto mt-10 md:mt-14">
<div class="flex flex-wrap sm:flex-nowrap sm:items-center gap-x-2 gap-y-1 sm:gap-y-0">
{post.tags.map((tag: string) => (
<span class="inline-flex items-center button-base bg-cobalt dark:bg-turquoise text-neutral-100 text-xs font-bold rounded-lg gap-x-1.5 px-3 py-1.5">
{tag}
</span>
))}
</div>
<SocialShareButton pageTitle={post.title}/>
</div>
</div>
</div>
</div>
</section>
<style is:inline>
code[data-theme*=' '],
code[data-theme*=' '] span {
color: var(--shiki-light);
}
html.dark {
code[data-theme*=' '],
code[data-theme*=' '] span {
color: var(--shiki-dark);
}
}
</style>
</BaseLayout>
<script>
// Add smooth reveal animations for content after loading
document.addEventListener('astro:page-load', () => {
const animateContent = () => {
const smoothReveal = document.querySelectorAll('.smooth-reveal');
smoothReveal.forEach((el, index) => {
setTimeout(
() => {
el.classList.add('animate-reveal');
},
100 + index * 100
);
});
};
animateContent();
});
</script>