personal-site/pages/blog.vue
2024-12-22 12:22:47 -08:00

191 lines
No EOL
5.8 KiB
Vue

<script setup>
import { onMounted, watch, ref } from 'vue';
import fm from 'front-matter';
import PostCard from '../components/PostCard.vue';
// Automatically maintained is a blog_list.json in assets/. This file contains a list of all blog posts and their metadata.
// This file is generated by a script in the root directory of the project.
import blog_list from '../assets/blog_list.json';
import configured_markdown from '~/assets/markdown_conf';
const modules = import.meta.glob('/blog/');
for (const path in modules) {
const module = await modules[path]();
console.log(path, module.default); // module.default contains the image data
}
let route = useRoute();
console.log(route)
const url = ref(null)
url.value = route.query.post
console.log(url.value)
const loading = ref(false)
const data = ref([])
const text = ref([])
const error = ref([])
const list = ref([])
const tagList = ref([])
const tagFilter = ref([])
tagFilter.value = []
const background = ref('');
const title = ref(null);
const description = ref(null);
const date = ref(null);
const tags = ref(null);
// watch the params of the route to fetch the data again
watch(route, async () => {
url.value = route.query.post
await fetchData(route.query.post)
}, {
immediate: true
});
async function fetchData(url) {
const md = configured_markdown();
error.value = data.value = null
loading.value = true
console.log(url)
try {
data.value = await $fetch(url)
console.log(data.value)
const processed = fm(data.value)
text.value = md.render(processed.body)
console.log(text.value)
background.value = processed.attributes.background
title.value = processed.attributes.title
description.value = processed.attributes.description
date.value = processed.attributes.date
tags.value = processed.attributes.tags
} catch (err) {
error.value = err.toString()
console.error(err)
loading.value = false
} finally {
loading.value = false
}
}
/* If tags are specified, include only posts with those tags */
/* else, include all posts */
async function fetchList() {
/*Example formatting:
{
"posts": [
{
"metadata": {
"test": "mrrp"
},
"id": "test",
"url": "/blog/test"
},
{
"metadata": {
" title": "Awesome", "description": "A curated list of awesome stuff I like", "date": "2024-11-26", "tags": ["awesome", "curated"]
},
"id": "awesome",
"url": "/blog/awesome"
},
{
"metadata": {
"test": "mrrp",
"test2": "nya"
},
"id": "test2",
"url": "/blog/test2"
}
]
}
*/
// Extract the posts
list.value = blog_list.posts
// Extract the tags
tagList.value = blog_list.posts.flatMap(post => post.metadata.tags).filter((tag, index, self) => self.indexOf(tag) === index)
}
fetchList()
// Hook and check if the URL gets changed in the query params
watch(url, async () => {
await fetchData(url.value)
}, {
immediate: true
});
</script>
<template>
<div class="relative z-50 flex w-full justify-center text-white">
<div class="mt-8 flex-col text-center">
<Transition name="list">
<div v-if="url == null" :key="url">
<h1>Blog</h1>
<div class="flex justify-center m-5">
<div v-for="tag in tagList" :key="tag" class="m-1 text-center">
<button
@click="tagFilter.includes(tag) ? tagFilter.splice(tagFilter.indexOf(tag), 1) : tagFilter.push(tag)"
class="text-xs bg-purple-800 border-purple-400 border text-white p-1 rounded-md"
:class="tagFilter.includes(tag) ? 'border-2 border-purple-200 bg-purple-500' : 'border-1 border-purple-600 bg-purple-800 text-white'">{{
tag }}</button>
</div>
</div>
<div>
<TransitionGroup name="list">
<div v-for="post in tagFilter.length == 0 ? list : list.filter(post => tagFilter.some(tag => post.metadata.tags.includes(tag)))" :key="post.id">
<PostCard :url="post.url" :key="post.id" />
</div>
</TransitionGroup>
</div>
</div>
<div v-else>
<Transition name="list">
<div class="flex flex-col" :key="url">
<h1>{{ title }}</h1>
<div class="max-w-50 flex flex-row justify-center">
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
<span class="text-xs bg-purple-800 border-purple-400 border text-white p-1 rounded-md">{{
tag }}</span>
</div>
</div>
<div
class="text-pretty min-w-96 text-left max-w-4xl mt-4 p-6 max-md:w-screen rounded-md container bg-opacity-90 bg-purple-950">
<div v-html="text"></div>
</div>
</div>
</Transition>
</div>
</Transition>
</div>
</div>
</template>
<style scoped>
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translate(100px, 0);
}
.list-leave-active {
position: absolute;
}
</style>