mirror of
https://github.com/ArthurDanjou/website.git
synced 2026-01-28 02:40:32 +01:00
@@ -7,169 +7,169 @@ const appConfig = useAppConfig()
|
||||
const route = useRoute()
|
||||
const { data: postContent } = await useAsyncData<Post>(`writing:${route.params.slug}`, () => queryContent<Post>(`/writing/${route.params.slug}`).findOne())
|
||||
const {
|
||||
data: post,
|
||||
data: post,
|
||||
} = await useFetch<PrismaPost>('/api/article', {
|
||||
method: 'post',
|
||||
body: {
|
||||
slug: route.params.slug.toString(),
|
||||
},
|
||||
method: 'post',
|
||||
body: {
|
||||
slug: route.params.slug.toString(),
|
||||
},
|
||||
})
|
||||
|
||||
const likes = ref(post.value?.likes)
|
||||
async function like() {
|
||||
const data = await $fetch<PrismaPost>('/api/like', {
|
||||
method: 'PUT',
|
||||
body: {
|
||||
slug: post.value?.slug,
|
||||
},
|
||||
})
|
||||
likes.value = data.likes
|
||||
const data = await $fetch<PrismaPost>('/api/like', {
|
||||
method: 'PUT',
|
||||
body: {
|
||||
slug: post.value?.slug,
|
||||
},
|
||||
})
|
||||
likes.value = data.likes
|
||||
}
|
||||
|
||||
if (!postContent.value) {
|
||||
throw showError({
|
||||
statusMessage: 'The post you are looking for was not found.',
|
||||
statusCode: 404,
|
||||
})
|
||||
throw showError({
|
||||
statusMessage: 'The post you are looking for was not found.',
|
||||
statusCode: 404,
|
||||
})
|
||||
}
|
||||
|
||||
const format = (date: string) => useDateFormat(date, 'D MMMM YYYY').value.replaceAll('"', '')
|
||||
useHead({
|
||||
title: `${postContent.value?.title} • Arthur Danjou's shelf`,
|
||||
title: `${postContent.value?.title} • Arthur Danjou's shelf`,
|
||||
})
|
||||
|
||||
function top() {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard({
|
||||
source: `https://arthurdanjou.fr/writing/${route.params.slug}`,
|
||||
copiedDuring: 4000,
|
||||
source: `https://arthurdanjou.fr/writing/${route.params.slug}`,
|
||||
copiedDuring: 4000,
|
||||
})
|
||||
|
||||
const likeCookie = useCookie<boolean>(`post:like:${postContent.value.slug}`, {
|
||||
maxAge: 604_800,
|
||||
maxAge: 604_800,
|
||||
})
|
||||
|
||||
async function handleLike() {
|
||||
await like()
|
||||
likeCookie.value = true
|
||||
await like()
|
||||
likeCookie.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
v-if="postContent && post"
|
||||
class="w-container lg:mt-24 mt-16"
|
||||
>
|
||||
<div class="lg:relative">
|
||||
<div class="max-w-3xl space-y-8 mx-auto">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<UButton
|
||||
icon="i-ph-arrow-circle-left-bold"
|
||||
variant="soft"
|
||||
size="lg"
|
||||
:ui="{ rounded: 'rounded-full' }"
|
||||
class="lg:absolute left-0 mb-8"
|
||||
@click.prevent="useRouter().back()"
|
||||
/>
|
||||
<article>
|
||||
<header class="flex flex-col space-y-6">
|
||||
<time class="flex items-center text-base text-zinc-400 dark:text-zinc-500">
|
||||
<span class="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
<div class="ml-3 flex gap-3">
|
||||
<div>
|
||||
{{ format(postContent.publishedAt) }}
|
||||
</div>
|
||||
<span>•</span>
|
||||
<div>{{ postContent.readingMins }} min</div>
|
||||
<span>•</span>
|
||||
<div>{{ post.views }} {{ post.views > 1 ? 'views' : 'view' }}</div>
|
||||
</div>
|
||||
</time>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{{ postContent.title }}
|
||||
</h1>
|
||||
<p class="text-subtitle">
|
||||
{{ postContent.description }}
|
||||
</p>
|
||||
</header>
|
||||
<div
|
||||
v-if="postContent.cover"
|
||||
class="w-full rounded-md my-8"
|
||||
>
|
||||
{{ postContent.cover }}
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<ContentRenderer
|
||||
class="mt-12 prose dark:prose-invert max-w-none prose-style"
|
||||
:class="`prose-${appConfig.ui.primary}`"
|
||||
:value="postContent"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="my-16 text-subtitle">
|
||||
<div class="flex gap-2 items-center">
|
||||
<UIcon name="i-eos-icons-loading" />
|
||||
<p>The content of the post is loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<footer class="my-8 space-y-8">
|
||||
<UDivider />
|
||||
<p class="text-subtitle">
|
||||
Thanks for reading this post! If you liked it, please consider sharing it with your friends. <strong>Don't forget to leave a like!</strong>
|
||||
</p>
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<UButton
|
||||
:label="`${likes} ${likes! > 1 ? 'likes' : 'like'}`"
|
||||
icon="i-ph-heart-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="handleLike()"
|
||||
/>
|
||||
<UButton
|
||||
label="Go to top"
|
||||
icon="i-ph-arrow-up-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="top()"
|
||||
/>
|
||||
<UButton
|
||||
label="Share on Twitter"
|
||||
icon="i-ph-twitter-logo-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
<UButton
|
||||
v-if="copied"
|
||||
label="Link copied"
|
||||
icon="i-lucide-clipboard-check"
|
||||
color="green"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
<UButton
|
||||
v-else
|
||||
label="Copy link"
|
||||
icon="i-lucide-clipboard"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
v-if="postContent && post"
|
||||
class="w-container lg:mt-24 mt-16"
|
||||
>
|
||||
<div class="lg:relative">
|
||||
<div class="max-w-3xl space-y-8 mx-auto">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<UButton
|
||||
icon="i-ph-arrow-circle-left-bold"
|
||||
variant="soft"
|
||||
size="lg"
|
||||
:ui="{ rounded: 'rounded-full' }"
|
||||
class="lg:absolute left-0 mb-8"
|
||||
@click.prevent="useRouter().back()"
|
||||
/>
|
||||
<article>
|
||||
<header class="flex flex-col space-y-6">
|
||||
<time class="flex items-center text-base text-zinc-400 dark:text-zinc-500">
|
||||
<span class="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
<div class="ml-3 flex gap-3">
|
||||
<div>
|
||||
{{ format(postContent.publishedAt) }}
|
||||
</div>
|
||||
<span>•</span>
|
||||
<div>{{ postContent.readingMins }} min</div>
|
||||
<span>•</span>
|
||||
<div>{{ post.views }} {{ post.views > 1 ? 'views' : 'view' }}</div>
|
||||
</div>
|
||||
</time>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{{ postContent.title }}
|
||||
</h1>
|
||||
<p class="text-subtitle">
|
||||
{{ postContent.description }}
|
||||
</p>
|
||||
</header>
|
||||
<div
|
||||
v-if="postContent.cover"
|
||||
class="w-full rounded-md my-8"
|
||||
>
|
||||
{{ postContent.cover }}
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<ContentRenderer
|
||||
class="mt-12 prose dark:prose-invert max-w-none prose-style"
|
||||
:class="`prose-${appConfig.ui.primary}`"
|
||||
:value="postContent"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="my-16 text-subtitle">
|
||||
<div class="flex gap-2 items-center">
|
||||
<UIcon name="i-eos-icons-loading" />
|
||||
<p>The content of the post is loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<footer class="my-8 space-y-8">
|
||||
<UDivider />
|
||||
<p class="text-subtitle">
|
||||
Thanks for reading this post! If you liked it, please consider sharing it with your friends. <strong>Don't forget to leave a like!</strong>
|
||||
</p>
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<UButton
|
||||
:label="`${likes} ${likes! > 1 ? 'likes' : 'like'}`"
|
||||
icon="i-ph-heart-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="handleLike()"
|
||||
/>
|
||||
<UButton
|
||||
label="Go to top"
|
||||
icon="i-ph-arrow-up-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="top()"
|
||||
/>
|
||||
<UButton
|
||||
label="Share on Twitter"
|
||||
icon="i-ph-twitter-logo-bold"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
<UButton
|
||||
v-if="copied"
|
||||
label="Link copied"
|
||||
icon="i-lucide-clipboard-check"
|
||||
color="green"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
<UButton
|
||||
v-else
|
||||
label="Copy link"
|
||||
icon="i-lucide-clipboard"
|
||||
size="lg"
|
||||
variant="soft"
|
||||
@click.prevent="copy()"
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -3,7 +3,7 @@ const appConfig = useAppConfig()
|
||||
const getColor = computed(() => `text-${appConfig.ui.primary}-500`)
|
||||
|
||||
useHead({
|
||||
title: 'My Shelf • Arthur Danjou',
|
||||
title: 'My Shelf • Arthur Danjou',
|
||||
})
|
||||
|
||||
const { data: posts } = await getPosts()
|
||||
@@ -11,59 +11,59 @@ const format = (date: string) => useDateFormat(date, 'D MMMM YYYY').value.replac
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="w-container lg:my-24 my-16">
|
||||
<div class="px-4 max-w-3xl space-y-8">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl !leading-tight">
|
||||
Writing on my life, development and my passions.
|
||||
</h1>
|
||||
<p class="leading-relaxed text-subtitle">
|
||||
All my thoughts on programming, mathematics, artificial intelligence design, etc., are put together in chronological order. I also write about my projects, my discoveries, and my thoughts. <s>It is sometimes updated.</s>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-16 md:mt-20">
|
||||
<div class="md:border-l md:border-zinc-100 md:pl-6 md:dark:border-zinc-700/40">
|
||||
<div class="flex max-w-3xl flex-col space-y-16">
|
||||
<article
|
||||
v-for="post in posts"
|
||||
:key="post.slug"
|
||||
class="px-6 md:grid md:grid-cols-4 md:items-baseline"
|
||||
>
|
||||
<div class="group md:col-span-3 group relative flex flex-col items-start">
|
||||
<h2 class="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
|
||||
<div class="absolute -inset-y-6 -inset-x-4 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 md:rounded-2xl" />
|
||||
<NuxtLink :to="post._path">
|
||||
<span class="absolute -inset-y-6 -inset-x-4 z-20 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<span class="relative z-10">
|
||||
{{ post.title }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</h2>
|
||||
<time class="md:hidden relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-3.5">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center">
|
||||
<span class="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
</span>
|
||||
{{ format(post.publishedAt) }}
|
||||
</time>
|
||||
<p class="relative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{{ post.description }}
|
||||
</p>
|
||||
<div
|
||||
:class="getColor"
|
||||
class="relative z-10 mt-4 flex items-center gap-2 justify-center text-sm font-medium"
|
||||
>
|
||||
<p>Read article</p>
|
||||
<UIcon name="i-ph-arrow-circle-right-bold" />
|
||||
</div>
|
||||
</div>
|
||||
<time class="mt-1 md:block relative z-10 order-first mb-3 hidden text-sm text-zinc-400 dark:text-zinc-500">
|
||||
<p>{{ format(post.publishedAt) }}</p>
|
||||
<p>{{ post.readingMins }} min.</p>
|
||||
</time>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-container lg:my-24 my-16">
|
||||
<div class="px-4 max-w-3xl space-y-8">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl !leading-tight">
|
||||
Writing on my life, development and my passions.
|
||||
</h1>
|
||||
<p class="leading-relaxed text-subtitle">
|
||||
All my thoughts on programming, mathematics, artificial intelligence design, etc., are put together in chronological order. I also write about my projects, my discoveries, and my thoughts. <s>It is sometimes updated.</s>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-16 md:mt-20">
|
||||
<div class="md:border-l md:border-zinc-100 md:pl-6 md:dark:border-zinc-700/40">
|
||||
<div class="flex max-w-3xl flex-col space-y-16">
|
||||
<article
|
||||
v-for="post in posts"
|
||||
:key="post.slug"
|
||||
class="px-6 md:grid md:grid-cols-4 md:items-baseline"
|
||||
>
|
||||
<div class="group md:col-span-3 group relative flex flex-col items-start">
|
||||
<h2 class="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
|
||||
<div class="absolute -inset-y-6 -inset-x-4 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 md:rounded-2xl" />
|
||||
<NuxtLink :to="post._path">
|
||||
<span class="absolute -inset-y-6 -inset-x-4 z-20 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<span class="relative z-10">
|
||||
{{ post.title }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</h2>
|
||||
<time class="md:hidden relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-3.5">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center">
|
||||
<span class="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
</span>
|
||||
{{ format(post.publishedAt) }}
|
||||
</time>
|
||||
<p class="relative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{{ post.description }}
|
||||
</p>
|
||||
<div
|
||||
:class="getColor"
|
||||
class="relative z-10 mt-4 flex items-center gap-2 justify-center text-sm font-medium"
|
||||
>
|
||||
<p>Read article</p>
|
||||
<UIcon name="i-ph-arrow-circle-right-bold" />
|
||||
</div>
|
||||
</div>
|
||||
<time class="mt-1 md:block relative z-10 order-first mb-3 hidden text-sm text-zinc-400 dark:text-zinc-500">
|
||||
<p>{{ format(post.publishedAt) }}</p>
|
||||
<p>{{ post.readingMins }} min.</p>
|
||||
</time>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user