:3
This commit is contained in:
parent
328a6bd178
commit
1dbf1e4074
21 changed files with 1046 additions and 545 deletions
275
pages/blog.vue
275
pages/blog.vue
|
@ -1,121 +1,61 @@
|
|||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, watch, ref } from 'vue';
|
||||
import fm from 'front-matter';
|
||||
|
||||
import PostCard from '../components/PostCard.vue';
|
||||
import { globalMarkdown, MarkdownInput, type MarkdownMetadata } from '~/assets/markdown_conf';
|
||||
import * as pages from '~/utils/pageupdater/update_pagelist';
|
||||
import type { PageInfo, PageInfoMetdata } from '~/utils/pageupdater/pages';
|
||||
|
||||
// Automatically maintained is a blog_list.json in assets/meta. This file contains a list of all blog posts and their metadata.
|
||||
// This file is generated by a script in the utils/pageupdater folder.
|
||||
const blog_list: pages.PageList = (await import('~/assets/meta/blog_list.json')) as pages.PageList;
|
||||
|
||||
|
||||
// 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';
|
||||
|
||||
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();
|
||||
let route = useRoute()
|
||||
console.log(route)
|
||||
|
||||
const url = ref(null)
|
||||
url.value = route.query.post
|
||||
const url: Ref<string> = ref("")
|
||||
url.value = route.query.post as string
|
||||
|
||||
console.log(url.value)
|
||||
|
||||
const loading = ref(false)
|
||||
const data = ref([])
|
||||
const text = ref([])
|
||||
const error = ref([])
|
||||
const loading: Ref<boolean> = ref(false)
|
||||
|
||||
const list = ref([])
|
||||
const tagList = ref([])
|
||||
const tagFilter = ref([])
|
||||
const list: Ref<any> = ref([])
|
||||
const tagList: Ref<any> = ref([])
|
||||
const tagFilter: Ref<string[]> = ref([])
|
||||
tagFilter.value = []
|
||||
|
||||
const article_contents: Ref<string> = ref("")
|
||||
|
||||
const background = ref('');
|
||||
const title = ref(null);
|
||||
const description = ref(null);
|
||||
const date = ref(null);
|
||||
const tags = ref(null);
|
||||
const previous = ref(null);
|
||||
const next = ref(null);
|
||||
const metadata: Ref<PageInfoMetdata> = ref({
|
||||
title: "",
|
||||
description: "",
|
||||
date: "",
|
||||
tags: [],
|
||||
background: ""
|
||||
})
|
||||
|
||||
// watch the params of the route to fetch the data again
|
||||
|
||||
watch(route, async () => {
|
||||
url.value = route.query.post
|
||||
await fetchData(route.query.post)
|
||||
url.value = route.query.post as string
|
||||
if (url.value) {
|
||||
console.log("Fetching article")
|
||||
loading.value = true
|
||||
try {
|
||||
await fetchArticle(url.value)
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
|
||||
async function fetchData(url) {
|
||||
error.value = data.value = null
|
||||
loading.value = true
|
||||
console.log(url)
|
||||
|
||||
try {
|
||||
data.value = await $fetch(url)
|
||||
const processed = fm(data.value)
|
||||
text.value = processed.body
|
||||
background.value = processed.attributes.background
|
||||
title.value = processed.attributes.title
|
||||
description.value = processed.attributes.description
|
||||
date.value = processed.attributes.date.toLocaleDateString()
|
||||
tags.value = processed.attributes.tags
|
||||
|
||||
if (processed.attributes.previous)
|
||||
previous.value = "/blog/?post=/blog/" + processed.attributes.previous
|
||||
else
|
||||
previous.value = null
|
||||
if (processed.attributes.next)
|
||||
next.value = "/blog/?post=/blog/" + processed.attributes.next
|
||||
else
|
||||
next.value = null
|
||||
} 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
|
||||
|
||||
|
@ -123,84 +63,109 @@ async function fetchList() {
|
|||
tagList.value = blog_list.posts.flatMap(post => post.metadata.tags).filter((tag, index, self) => self.indexOf(tag) === index)
|
||||
|
||||
// Sort the posts by date, most recent first
|
||||
list.value.sort((a, b) => new Date(b.metadata.date) - new Date(a.metadata.date))
|
||||
list.value.sort((a: any, b: any) => b.metadata.date.localeCompare(a.metadata.date))
|
||||
}
|
||||
fetchList()
|
||||
|
||||
// Fetch the article contents from the URL
|
||||
async function fetchArticle(url: string) {
|
||||
const post = blog_list.posts.find(post => post.url === url)
|
||||
if (post) {
|
||||
const response = await fetch(post.url)
|
||||
article_contents.value = await response.text()
|
||||
}
|
||||
}
|
||||
|
||||
function resetReadingPosition() {
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
// Hook and check if the URL gets changed in the query params
|
||||
watch(url, async () => {
|
||||
await fetchData(url.value)
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
function updateMetadata(meta: MarkdownMetadata) {
|
||||
metadata.value = meta
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative z-50 flex w-full justify-center text-white">
|
||||
<!-- Metadata -->
|
||||
<MetaSet :title="metadata.title" :description="metadata.description" :date="metadata.date"
|
||||
:background="metadata.background" :tags="metadata.tags" />
|
||||
|
||||
<!-- Main Content -->
|
||||
<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-black border-purple-400 border text-white p-1 rounded-md"
|
||||
:class="tagFilter.includes(tag) ? 'border-2 border-white bg-slate-700' : 'border-2 bg-black text-white'">{{
|
||||
tag }}</button>
|
||||
<!-- Article List -->
|
||||
<div v-if="url == null" :key="url">
|
||||
<h1>Blog</h1>
|
||||
<div class="flex justify-center">
|
||||
|
||||
<div class="flex flex-wrap justify-center m-5 max-w-96">
|
||||
<div v-for="tag in tagList" :key="tag" class="m-1">
|
||||
<button
|
||||
@click="tagFilter.includes(tag) ? tagFilter.splice(tagFilter.indexOf(tag), 1) : tagFilter.push(tag)"
|
||||
class="text-xs bg-black border-purple-400 border text-white p-1 rounded-md"
|
||||
:class="tagFilter.includes(tag) ? 'border-2 border-white bg-slate-700' : 'border-2 bg-black text-white'">{{
|
||||
tag }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<TransitionGroup name="list">
|
||||
<div
|
||||
v-for="post in tagFilter.length == 0 ? list : list.filter((post: PageInfo) => post.metadata.tags ? post.metadata.tags.some(tag => tagFilter.includes(tag)) : false)">
|
||||
<PostCard :url="post.url" key="{{post.id}}" :tagFilter="tagFilter" />
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</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" :tagFilter="tagFilter"/>
|
||||
<!-- Article Viewer -->
|
||||
<div v-else>
|
||||
<Transition name="list">
|
||||
<div class="flex flex-col" :key="url">
|
||||
<h1>{{ metadata.title }}</h1>
|
||||
<small>{{ metadata.date ? new Date(metadata.date).toLocaleDateString() : "" }}</small>
|
||||
<div class="max-w-50 flex flex-row justify-center">
|
||||
<div v-for="tag in metadata.tags" :key="tag" class="m-1 text-center">
|
||||
<span
|
||||
class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{
|
||||
tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Next/Prev controls, on the left and right side using PostCards -->
|
||||
<div class="flex max-w-4xl max-md:w-screen">
|
||||
<div class="justify-start">
|
||||
<NuxtLink v-if="metadata.previous" :onclick="resetReadingPosition" :to="metadata.previous"
|
||||
class="m-2 text-white">Previous</NuxtLink>
|
||||
</div>
|
||||
<div class="justify-end">
|
||||
<NuxtLink v-if="metadata.next" :onclick="resetReadingPosition" :to="metadata.next"
|
||||
class="m-2 text-white">Next</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Article Content -->
|
||||
<Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left">
|
||||
<article>
|
||||
<Markdown :input="article_contents" type="markdown" @metadata="updateMetadata" />
|
||||
<!-- Aligned next/prev controls -->
|
||||
<div class="flex">
|
||||
<div class="justify-start">
|
||||
<NuxtLink v-if="metadata.previous" :onclick="resetReadingPosition" :to="metadata.previous"
|
||||
class="m-2 text-white">Previous</NuxtLink>
|
||||
</div>
|
||||
<div class="justify-end">
|
||||
<NuxtLink v-if="metadata.next" :onclick="resetReadingPosition" :to="metadata.next"
|
||||
class="m-2 text-white">Next</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</Card>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<Transition name="list">
|
||||
<div class="flex flex-col" :key="url">
|
||||
<h1>{{ title }}</h1>
|
||||
<small>{{ date }}</small>
|
||||
<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-black border-purple-400 border-2 text-white p-1 rounded-md">{{
|
||||
tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Next/Prev controls, on the left and right side using PostCards -->
|
||||
<div class="flex max-w-4xl max-md:w-screen">
|
||||
<div class="justify-start">
|
||||
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous" class="m-2 text-white">Previous</NuxtLink>
|
||||
</div>
|
||||
<div class="justify-end">
|
||||
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next" class="m-2 text-white">Next</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left">
|
||||
<Markdown :text="text"></Markdown>
|
||||
<!-- Aligned next/prev controls -->
|
||||
<div class="flex">
|
||||
<div class="justify-start">
|
||||
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous" class="m-2 text-white">Previous</NuxtLink>
|
||||
</div>
|
||||
<div class="justify-end">
|
||||
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next" class="m-2 text-white">Next</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</Transition>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import markdownit from 'markdown-it'
|
||||
import PostCard from '../components/PostCard.vue';
|
||||
import configured_markdown from '~/assets/markdown_conf';
|
||||
import Markdown from '~/components/Markdown.vue';
|
||||
import Card from '~/components/Card.vue';
|
||||
import * as siteConfig from "../assets/config.ts";
|
||||
import MetaSet from '~/components/MetaSet.vue';
|
||||
|
||||
const aboutMe = ref("");
|
||||
|
@ -20,25 +16,19 @@ fetch("/about_me.md")
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MetaSet title="Home" description="TheFelidae's personal site :3" tags="home, personal, author"/>
|
||||
|
||||
<div class="relative flex w-full justify-center text-white">
|
||||
<MetaSet title="Home" description="TheFelidae's personal site :3" tags="home, personal, author"/>
|
||||
|
||||
<div class="mt-8 flex-col text-center">
|
||||
<div class="flex justify-center">
|
||||
<div id="PFP" class="p-1 shadow-md rounded-full bg-pink-500">
|
||||
<div id="PFP" class="shadow-md rounded-full shadow-highlight">
|
||||
<img class="transition-all w-40 h-40 md:w-56 md:h-56 rounded-full"
|
||||
src="https://avatars.githubusercontent.com/u/94077364?v=4" alt="User PFP" />
|
||||
</div>
|
||||
</div>
|
||||
<Card class="max-w-4xl mt-4 max-md:w-screen">
|
||||
<Markdown :text="aboutMe"></Markdown>
|
||||
<Markdown :input="aboutMe" type="markdown"></Markdown>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#PFP {
|
||||
box-shadow: 0 0 10px 0 pink;
|
||||
}
|
||||
</style>
|
||||
</template>
|
Loading…
Add table
Add a link
Reference in a new issue