show top tags on index page

This commit is contained in:
2024-04-30 23:10:12 +02:00
parent 76085120c1
commit 8f5069e207
11 changed files with 175 additions and 43 deletions

View File

@ -10,6 +10,7 @@ mod pages;
mod post_list;
mod post_parser;
mod router;
mod tag_list;
// mod template;
#[tokio::main]

View File

@ -1,8 +1,12 @@
use askama::Template;
use axum::http::StatusCode;
use crate::components::{
site_footer::{render_site_footer, SiteFooter},
site_header::HeaderProps,
use crate::{
components::{
site_footer::{render_site_footer, SiteFooter},
site_header::HeaderProps,
},
tag_list::get_popular_blog_tags,
};
#[derive(Template)]
@ -10,12 +14,24 @@ use crate::components::{
pub struct IndexTemplate {
site_footer: SiteFooter,
header_props: HeaderProps,
blog_tags: Vec<String>,
}
pub async fn render_index() -> IndexTemplate {
let site_footer = render_site_footer().await;
IndexTemplate {
pub async fn render_index() -> Result<IndexTemplate, StatusCode> {
let site_footer = tokio::spawn(render_site_footer());
let blog_tags = tokio::spawn(get_popular_blog_tags());
let blog_tags = blog_tags
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)??;
let site_footer = site_footer
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(IndexTemplate {
site_footer,
header_props: HeaderProps::default(),
}
blog_tags,
})
}

View File

@ -51,7 +51,6 @@ pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTempl
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// TODO if we have a tag we want to go back to all posts, otherwise we don't
let header_props = match tag {
Some(_) => HeaderProps::with_back_link(Link {
href: "/blog".to_string(),
@ -68,6 +67,3 @@ pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTempl
header_props,
})
}
// TODO Do we want pagination or not? Ask designer/ We don't want itt
// TODO when tags are true render different "see all post" message

View File

@ -0,0 +1,34 @@
use crate::{pages::post::PostMetadata, post_list::get_post_list};
use axum::http::StatusCode;
use std::collections::HashMap;
use tracing::debug;
pub async fn get_popular_blog_tags() -> Result<Vec<String>, StatusCode> {
const TAGS_LENGTH: usize = 7;
let post_list = get_post_list::<PostMetadata>().await?;
let tags_sum = post_list
.into_iter()
.flat_map(|post| post.metadata.tags)
.fold(HashMap::new(), |mut acc, tag| {
*acc.entry(tag).or_insert(0) += 1;
acc
});
let mut sorted_tags_by_count: Vec<_> = tags_sum.into_iter().collect();
sorted_tags_by_count.sort_by_key(|&(_, count)| std::cmp::Reverse(count));
// Log the counts
for (tag, count) in &sorted_tags_by_count {
debug!("Tag: {}, Count: {}", tag, count);
}
let popular_blog_tags = sorted_tags_by_count
.into_iter()
.map(|tag_count| tag_count.0)
.filter(|tag| tag != "dev")
.take(TAGS_LENGTH)
.collect::<Vec<String>>();
Ok(popular_blog_tags)
}

View File

@ -2,6 +2,14 @@
@tailwind components;
@tailwind utilities;
a {
@apply text-pink-600 underline underline-offset-2;
&:hover {
@apply transition text-blue-400;
}
}
.article-body {
h1 {
@apply px-4 text-2xl text-blue-900 my-2;

View File

@ -562,16 +562,50 @@ video {
margin: 1rem;
}
.m-5 {
margin: 1.25rem;
}
.mx-0 {
margin-left: 0px;
margin-right: 0px;
}
.mx-0\.5 {
margin-left: 0.125rem;
margin-right: 0.125rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mx-4 {
margin-left: 1rem;
margin-right: 1rem;
}
.mx-5 {
margin-left: 1.25rem;
margin-right: 1.25rem;
}
.mx-6 {
margin-left: 1.5rem;
margin-right: 1.5rem;
}
.my-0 {
margin-top: 0px;
margin-bottom: 0px;
}
.my-0\.5 {
margin-top: 0.125rem;
margin-bottom: 0.125rem;
}
.my-12 {
margin-top: 3rem;
margin-bottom: 3rem;
@ -587,16 +621,6 @@ video {
margin-bottom: 1.5rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
@ -605,6 +629,10 @@ video {
margin-bottom: 0.75rem;
}
.mb-5 {
margin-bottom: 1.25rem;
}
.mt-3 {
margin-top: 0.75rem;
}
@ -613,6 +641,10 @@ video {
display: block;
}
.inline-block {
display: inline-block;
}
.inline {
display: inline;
}
@ -707,6 +739,11 @@ video {
border-color: rgb(59 130 246 / var(--tw-border-opacity));
}
.border-blue-950 {
--tw-border-opacity: 1;
border-color: rgb(23 37 84 / var(--tw-border-opacity));
}
.bg-blue-50 {
--tw-bg-opacity: 1;
background-color: rgb(239 246 255 / var(--tw-bg-opacity));
@ -726,6 +763,14 @@ video {
fill: #172554;
}
.p-0 {
padding: 0px;
}
.p-0\.5 {
padding: 0.125rem;
}
.p-3 {
padding: 0.75rem;
}
@ -740,16 +785,6 @@ video {
padding-right: 1.25rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
@ -805,6 +840,10 @@ video {
font-weight: 600;
}
.capitalize {
text-transform: capitalize;
}
.italic {
font-style: italic;
}
@ -843,6 +882,11 @@ video {
color: rgb(31 41 55 / var(--tw-text-opacity));
}
.text-pink-950 {
--tw-text-opacity: 1;
color: rgb(80 7 36 / var(--tw-text-opacity));
}
.drop-shadow-md {
--tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@ -858,6 +902,24 @@ video {
transition-duration: 150ms;
}
a {
--tw-text-opacity: 1;
color: rgb(219 39 119 / var(--tw-text-opacity));
text-decoration-line: underline;
text-underline-offset: 2px;
&:hover {
--tw-text-opacity: 1;
color: rgb(96 165 250 / var(--tw-text-opacity));
}
&:hover {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
}
.article-body {
h1 {
margin-top: 0.5rem;
@ -959,11 +1021,6 @@ video {
margin: 1rem;
}
.hover\:bg-blue-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(191 219 254 / var(--tw-bg-opacity));
}
.hover\:bg-pink-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(251 207 232 / var(--tw-bg-opacity));

View File

@ -10,7 +10,7 @@
<header>
<h3 class="text-lg font-medium mb-1">{{heading}}</h3>
</header>
<p class="text-sm leading-5 text-gray-800">{{description}}</p>
<p class="text-sm leading-5 text-gray-800">{{description|safe}}</p>
</section>
</section>

View File

@ -22,20 +22,38 @@
</p>
</header>
<h2 class="text-blue-950 font-semibold text-2xl">About me</h2>
<h2 class="text-blue-950 font-semibold text-2xl m-5">About me</h2>
<p>
<p class="mx-5">
Welcome to my personal website. My name is
<strong>Michal&nbsp;Vanko</strong>
and I'm a
<em> <a href="https://en.wikipedia.org/wiki/Programmer">programmer</a> </em>
. I am developing software for more than half of my life and <strong>I love it!</strong> Sometimes I stream working on my side projects and building a <a href="TODO DISCORD">community of like minded people</a>. Here you can find blogs of my thoughts and journeys, as well as links to my socials where you can see other content.</p>
. I am developing software for more than half of my life and <strong>I love it!</strong> Sometimes I stream working on my side projects and building a <a href="https://discord.gg/2cGg7kwZEh">community of like minded people</a>. Here you can find blogs of my thoughts and journeys, as well as links to my socials where you can see other content.</p>
<section class="talent-cards">
<section id="talent-cards">
{% call tc::talent_card("code", "Web development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("gamepad", "Game development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("person-chalkboard", "Mentoring & Consulting", "I offer consulting sessions to assist you in developing <strong>higher-quality software</strong> and share insights from crafting robust, professional web applications. <a href=\"TODO callendly\">Schedule a session with me</a> and elevate your projects together.") %}
</section
<section id="blog">
<h2 class="text-blue-950 font-semibold text-2xl m-5">Blog</h2>
<section id="tags">
<ul class="mx-5">
{% for tag in blog_tags %}
<li class="inline-block mx-0.5 p-0.5">
<a href="/blog/tags/{{tag}}" class="text-pink-950">#{{tag|capitalize}}</a>
</li>
{% endfor %}
</ul>
</section>
<hr class="border-blue-950 m-5">
<ul class="mx-5">
</ul>
</section>
<section class="twitch-stream-promo">
<h2>Follow my twitch stream</h2>

View File

@ -1,4 +1,4 @@
<header class="min-h-full bg-blue-50">
<header class="min-h-full bg-blue-50 mb-5">
<nav class="flex">
{% match header_props.back_link %}
{% when Some with (link) %}
@ -16,4 +16,5 @@
</a>
</aside>
</nav>
<hr class="border-blue-950 mx-5">
</header>