This commit is contained in:
2021-08-23 19:14:23 +02:00
parent 2f50ad9d55
commit cfc0ed1ea4
40 changed files with 381 additions and 234 deletions

View File

@@ -1,6 +1,7 @@
<template>
<main v-if="post" class="blog flex flex-col items-center px-4 xl:px-72 mb-16 md:mb-32">
<main v-if="post && postData" class="blog flex flex-col items-center px-4 xl:px-72 mb-16 md:mb-32">
<div class="mt-8 md:mt-32 flex flex-col justify-around py-8 w-full">
{{ slug }}
<div>
<div class="mb-4 flex">
<nuxt-link to="/blog" class="back-arrow flex">
@@ -14,10 +15,10 @@
</div>
</div>
<h1 class="text-3xl md:text-5xl font-bold">
{{ post.title }}
{{ $t(postData.title.code) }}
</h1>
<h3 class="text-xl text-gray-800 dark:text-gray-300 my-4 md:mt-8">
{{ post.description }}
{{ $t(postData.description.code) }}
</h3>
<div class="flex flex-row justify-between w-full md:w-2/3 mb-12">
<div>
@@ -26,16 +27,16 @@
</div>
<div>
<p class="uppercase text-sm font-bold text-gray-800 dark:text-gray-400">{{ $t('blog.read.time') }}</p>
<p>{{ post.reading_time }} min</p>
<p>{{ postData.reading_time }} min</p>
</div>
<div>
<p :class="post.tags.length === 0 ? 'opacity-0': 'opacity-100'" class="uppercase text-sm font-bold text-gray-800 dark:text-gray-400">Tags</p>
<p :class="postData.tags.length === 0 ? 'opacity-0': 'opacity-100'" class="uppercase text-sm font-bold text-gray-800 dark:text-gray-400">Tags</p>
<p>{{ formatTags }}</p>
</div>
</div>
<div class="w-full">
<div class="flex justify-center w-full">
<img class="w-full" :src="require(`@/assets/images/posts/${post.cover}`)" alt="Cover Img" />
<img class="w-full" :src="`https://athena.arthurdanjou.fr/files/${postData.cover.file_name}`" alt="Cover Img" />
</div>
</div>
<nuxt-content
@@ -55,7 +56,7 @@
:class="liked ? 'border-red-500 dark:border-red-500 hover:border-gray-400 dark:hover:border-dark-200' : 'border-gray-400 dark:border-dark-200 hover:border-red-500 dark:hover:border-red-500'"
>
<div class="mr-2 lining-nums leading-3">
{{ likes }}
{{ getLikes }}
</div>
<div class="inline leading-6" :class="{'animate-pulse heartbeat': liked}">
<HeartIcon :liked="liked"/>
@@ -63,7 +64,7 @@
</div>
<a
target="_blank"
:href="'https://twitter.com/intent/tweet?url=https%3A%2F%2Farthurdanjou.fr%2Fblog%2F' + this.post.slug + '&text=' + $t('blog.tweet') + ' ' + post.title"
:href="'https://twitter.com/intent/tweet?url=https%3A%2F%2Farthurdanjou.fr%2Fblog%2F' + postData.slug + '&text=' + $t('blog.tweet') + ' ' + $i18n.t('title')"
class="mr-2 icon-hover cursor-pointer duration-300 text-2xl p-1 border-solid border border-gray-300 dark:border-dark-200 hover:border-cyan-500 dark:hover:border-cyan-400 flex justify-center items-center"
>
<TwitterBlogIcon />
@@ -119,7 +120,7 @@ export default defineComponent({
const slug = computed(() => route.value.params.slug)
const post = useStatic((slug) => {
return $content(`articles/${i18n.locale}`, slug)
return $content(i18n.locale, slug)
.fetch<Post>()
.catch((error) => {
app.error({statusCode: 404, message: "Post not found"})
@@ -127,31 +128,34 @@ export default defineComponent({
}) as Promise<Post>
}, slug, 'post')
title.value = post.value?.title ? `Blog - Arthur Danjou - ${post.value!.title}` : 'Loading title...'
watch(post, () => {
title.value = post.value?.title ? `Blog - Arthur Danjou - ${post.value!.title}` : 'Loading title...'
})
const liked = ref($storage.getCookie(`${slug.value}`) !== undefined)
const likes = ref(0)
const getLikes = computed(() => likes.value)
const likes = useAsync(async () => {
const response = await $axios.get(`/api/posts/${slug.value}`, {
const postData = useAsync(async () => {
const response = await $axios.get(`/api/posts/${slug.value}/data`, {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
})
if (response.status === 200) {
return response.data.likes
likes.value = response.data.post.likes
title.value = `Blog - Arthur Danjou - ${i18n.t(response.data.post.title.code)}`
return response.data.post
} else {
$sentry.captureEvent(response.data)
app.error({statusCode: 500})
}
}, 'likes')
}, 'postData')
watch(postData, () => {
title.value = `Blog - Arthur Danjou - ${i18n.t(postData.value.title.code)}`
likes.value = postData.value.likes
})
const handleLike = async () => {
if (liked.value) {
const response = await $axios.post(`/api/posts/${post.value?.slug}/unlike`, {}, {
const response = await $axios.post(`/api/posts/${postData.value.slug}/unlike`, {}, {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
@@ -165,7 +169,7 @@ export default defineComponent({
app.error({statusCode: 500})
}
} else {
const response = await $axios.post(`/api/posts/${post.value?.slug}/like`, {}, {
const response = await $axios.post(`/api/posts/${postData.value.slug}/like`, {}, {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
@@ -185,7 +189,7 @@ export default defineComponent({
const isCopied = ref(false)
const copyToClipboard = () => {
navigator.clipboard.writeText('https://arthurdanjou.fr/blog/' + post.value?.slug)
navigator.clipboard.writeText('https://arthurdanjou.fr/blog/' + postData.value.slug)
isCopied.value = true
setTimeout(() => {
isCopied.value = false
@@ -200,13 +204,13 @@ export default defineComponent({
}
const formatDate = computed(() => {
const [first, second, third]: any = post.value?.date.split('-')
const [first, second, third]: any = postData.value.date.split('-')
return `${first} ${i18n.t(`month.${second}`)} ${third}`
})
const formatTags = computed(() => {
let tags = ""
post.value?.tags.map(tag => {
postData.value.tags.map(tag => {
tags += i18n.t(String(tag)) + ", "
})
return tags.substring(0, tags.length - 2)
@@ -214,14 +218,16 @@ export default defineComponent({
return {
post,
likes,
getLikes,
liked,
handleLike,
isCopied,
copyToClipboard,
scrollToTop,
formatDate,
formatTags
formatTags,
postData,
slug
}
}
})

View File

@@ -9,9 +9,9 @@
<div class="flex py-8 w-full flex-wrap">
<div class="md:mx-8 my-4 w-full lg:w-auto" v-for="post in posts">
<Post
:title="post.title"
:cover="post.cover"
:description="post.description"
:title="post.title.code"
:cover="post.cover.file_name"
:description="post.description.code"
:date="post.date"
:slug="post.slug"
:tags="post.tags"
@@ -41,20 +41,25 @@ export default defineComponent({
}
},
setup() {
const { $content, i18n, $sentry } = useContext()
const { $sentry, $axios, app } = useContext()
const posts = useAsync(() => {
return $content(`articles/${i18n.locale}`)
.sortBy('date', 'asc')
.limit(10)
.fetch<Post>()
.catch((error) => {
$sentry.captureEvent(error)
})
const posts = useAsync(async () => {
const response = await $axios.get('/api/posts', {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
})
if (response.status === 200) {
console.log(response.data.posts)
return response.data.posts
} else {
app.error({statusCode: 500})
$sentry.captureEvent(response.data)
}
}, 'posts')
return {
posts,
posts
}
}
})

107
src/pages/maintenance.vue Normal file
View File

@@ -0,0 +1,107 @@
<template>
<div v-if="maintenance" class="w-screen h-screen flex items-center">
<div class="absolute right-4 top-4">
<ul class="flex items-center">
<li @click="changeLanguage()"
class="mx-1 h-9 w-9 cursor-pointer flex items-center justify-center p-1.5 rounded-xl hover:bg-gray-300 duration-200 dark:hover:bg-dark-400">
<TranslateIcon :french="isFrench"/>
</li>
<li @click="changeColorMode()"
class="mx-1 h-9 w-9 cursor-pointer flex items-center p-1.5 rounded-xl hover:bg-gray-300 dark:hover:bg-dark-400 duration-200">
<div v-if="this.$colorMode.preference === 'light'">
<MoonIcon/>
</div>
<div v-else>
<SunIcon/>
</div>
</li>
</ul>
</div>
<div class="w-full flex flex-col items-center">
<div class="self-center">
<div class="flex justify-center mb-8">
<img class="w-full lg:w-3/4 xl:w-1/3" src="@/assets/images/maintenance.png" alt="Maintenance Image" />
</div>
<div class="text-center">
<h3 class="text-xl lg:text-2xl">
{{ $t('maintenance.back_soon') }}
</h3>
<h1 class="font-black text-3xl lg:text-5xl my-4">
{{ $t('maintenance.title') }}
</h1>
<p class="text-gray-600 dark:text-gray-400 text-md lg:text-xl">
{{ $t(maintenance.reason.code) }}
</p>
</div>
</div>
<div class="lg:w-2/3 p-4 lg:p-8 mt-auto w-full ">
<div class="py-4 text-center border-t-2 border-gray-200 dark:border-gray-800 social-links">
{{ $t('maintenance.progress') }} <br class="lg:hidden"/>
<a target="_blank" href="https://twitter.com/ArthurDanj" rel="noopener noreferrer">
<TwitterIcon />
</a>
<div class="mx-2 inline">{{ $t('maintenance.separator') }}</div>
<a target="_blank" href="https://discord.gg/ENG6cFQhPS" rel="noopener noreferrer">
<DiscordIcon />
</a>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import {computed, defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
export default defineComponent({
name: "maintenance",
layout: 'maintenance',
setup() {
const {$axios, $sentry, app} = useContext()
const maintenance = useAsync(async () => {
const response = await $axios.get('/api/maintenance', {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
})
if (response.status === 200) {
return response.data.maintenance
} else {
$sentry.captureEvent(response.data)
app.error({statusCode: 500})
}
}, 'maintenance')
const {i18n} = useContext()
const changeLanguage = () => useAsync(() => {
i18n.setLocale(i18n.locale === 'fr' ? 'en' : 'fr')
})
const isFrench = computed(() => i18n.locale === 'fr')
const {$colorMode} = useContext()
const changeColorMode = () => {
$colorMode.preference = $colorMode.value === 'light' ? 'dark' : 'light'
}
return {
maintenance,
changeLanguage,
isFrench,
changeColorMode
}
}
})
</script>
<style lang="scss">
.social-links a {
svg {
@apply h-6 w-6 duration-300
}
&:hover svg {
@apply transform hover:scale-120
}
}
</style>

View File

@@ -5,12 +5,12 @@
<div v-else class="flex flex-col justify-around items-center py-10 w-full">
<h1 class="text-gray-700 dark:text-gray-400 text-xl mt-4">{{ $t('projects.description') }}</h1>
<div class="flex flex-col items-center md:items-start md:flex-row flex-wrap w-full space-y-3 md:space-y-0">
<div class="flex py-4 w-full flex-wrap justify-center">
<div class="md:mx-4 my-4 w-full lg:w-auto" v-for="project in projects">
<div class="lg:flex py-4 w-full flex-wrap justify-center">
<div class="m-4" v-for="project in projects">
<Project
:title="project.title"
:cover="project.cover"
:description="project.description"
:title="project.name"
:cover="project.cover.file_name"
:description="project.description.code"
:tags="project.tags"
:url="project.url"
/>
@@ -33,14 +33,20 @@ export default defineComponent({
}
},
setup() {
const { $content, $sentry } = useContext()
const { $axios, app, $sentry } = useContext()
const projects = useAsync(() => {
return $content('projects')
.fetch<Project>()
.catch((error) => {
$sentry.captureEvent(error)
})
const projects = useAsync(async () => {
const response = await $axios.get('/api/projects', {
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`
}
})
if (response.status === 200) {
return response.data.projects
} else {
$sentry.captureEvent(response.data)
app.error({statusCode: 500})
}
}, 'projects')
return {