implement blog tag filtering

This commit is contained in:
cel 🌸 2023-06-22 15:35:18 +01:00
parent 139c26158b
commit a135acf943
Signed by: cel
GPG Key ID: 48E29AF13B5F1349
7 changed files with 80 additions and 41 deletions

View File

@ -5,6 +5,7 @@ mod scrobbles;
mod skweets; mod skweets;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashSet;
use rocket::fs::{relative, FileServer}; use rocket::fs::{relative, FileServer};
use rocket::http::Status; use rocket::http::Status;
@ -47,24 +48,6 @@ async fn home(clients: &State<Clients>) -> Template {
) )
} }
#[get("/blog")]
async fn blog() -> Template {
let mut blogposts = posts::get_blogposts().await.unwrap_or_default();
let tags = posts::get_tags(&blogposts);
for blogpost in &mut blogposts {
blogpost.render().await;
}
let reverse = "reverse".to_owned();
Template::render(
"blog",
context! {
reverse,
blogposts,
tags,
},
)
}
#[get("/blog/<blogpost>")] #[get("/blog/<blogpost>")]
async fn blogpost(blogpost: &str) -> Result<Template> { async fn blogpost(blogpost: &str) -> Result<Template> {
let mut blogpost = posts::get_blogpost(blogpost).await?; let mut blogpost = posts::get_blogpost(blogpost).await?;
@ -77,6 +60,33 @@ async fn blogpost(blogpost: &str) -> Result<Template> {
)) ))
} }
#[get("/blog?<filter>")]
async fn blog(filter: Vec<String>) -> Result<Template> {
let mut blogposts = posts::get_blogposts().await?;
let tags: Vec<String> = posts::get_tags(&blogposts)
.into_iter()
.map(|tag| tag.to_owned())
.collect();
let mut filter_hashset: HashSet<String> = HashSet::new();
if !filter.is_empty() {
filter_hashset.extend(filter.into_iter());
blogposts = posts::filter_by_tags(blogposts, &filter_hashset);
}
for blogpost in &mut blogposts {
blogpost.render().await?;
}
let reverse = true;
Ok(Template::render(
"blog",
context! {
reverse,
tags,
filter_hashset,
blogposts,
},
))
}
#[get("/contact")] #[get("/contact")]
async fn contact() -> Template { async fn contact() -> Template {
Template::render("contact", context! {}) Template::render("contact", context! {})

View File

@ -1,3 +1,6 @@
mod article;
mod note;
use std::collections::HashSet; use std::collections::HashSet;
use async_trait::async_trait; use async_trait::async_trait;
@ -16,12 +19,6 @@ enum PostType {
Note, Note,
} }
enum TextFormat {
Markdown,
Plaintext,
Html,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Post<D: Content> { pub struct Post<D: Content> {
// id: i64, // id: i64,
@ -34,7 +31,7 @@ pub struct Post<D: Content> {
data: D, data: D,
} }
impl<D: Content + Serialize> Post<D> { impl<D: Content> Post<D> {
pub async fn render(&mut self) -> Result<()> { pub async fn render(&mut self) -> Result<()> {
self.render = Some(self.data.render().await?); self.render = Some(self.data.render().await?);
Ok(()) Ok(())
@ -74,6 +71,8 @@ pub async fn get_blogposts() -> Result<Vec<Post<Article>>> {
let blogpost = Post::try_from(blogpost).await.unwrap(); let blogpost = Post::try_from(blogpost).await.unwrap();
blogposts.push(blogpost); blogposts.push(blogpost);
} }
blogposts.sort_by_key(|post| post.created_at);
blogposts.reverse();
Ok(blogposts) Ok(blogposts)
} }
@ -87,21 +86,45 @@ pub async fn get_blogpost(post_name: &str) -> Result<Post<Article>> {
path: file.path().to_str().unwrap_or_default().to_owned(), path: file.path().to_str().unwrap_or_default().to_owned(),
name: name.to_owned(), name: name.to_owned(),
}; };
let blogpost = Post::try_from(blogpost).await.unwrap(); let blogpost = Post::try_from(blogpost).await?;
return Ok(blogpost); return Ok(blogpost);
} }
} }
Err(BlossomError::NotFound(Status::new(404))) Err(BlossomError::NotFound(Status::new(404)))
} }
pub fn get_tags<D: Content>(posts: &Vec<Post<D>>) -> HashSet<String> { pub fn get_tags<D: Content>(posts: &Vec<Post<D>>) -> Vec<&String> {
posts.into_iter().fold(HashSet::new(), |mut acc, post| { let mut tags = posts
.into_iter()
.fold(HashSet::new(), |mut acc, post| {
let tags = &post.tags; let tags = &post.tags;
for tag in tags { for tag in tags {
acc.insert(tag.to_owned()); acc.insert(tag);
} }
acc acc
}) })
.into_iter()
.collect::<Vec<_>>();
tags.sort();
tags
}
pub fn filter_by_tags<'p, D: Content>(
posts: Vec<Post<D>>,
filter_tags: &HashSet<String>,
) -> Vec<Post<D>> {
posts
.into_iter()
.filter(|post| {
for tag in &post.tags {
match filter_tags.contains(tag) {
true => return true,
false => continue,
}
}
false
})
.collect()
} }
#[async_trait] #[async_trait]

View File

@ -1 +1,7 @@
struct note enum TextFormat {
Markdown,
Plaintext,
Html,
}
struct Note {}

View File

@ -329,9 +329,9 @@ iframe {
} }
.tags { .tags {
display: flexbox; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5em; gap: 1em;
} }
.tags a { .tags a {

View File

@ -56,7 +56,7 @@
</ul> </ul>
</nav> </nav>
<main class="{% if reverse %}{{ reverse }}{% endif %}"> <main class="{% if reverse %}reverse{% endif %}">
<div class="main-content"> <div class="main-content">
{% block content %} {% block content %}

View File

@ -1,7 +1,7 @@
<div class="panel content blogpost"> <div class="panel content blogpost">
<h1 class="title">{{ blogpost.subject }}</h1> <h1 class="title">{{ blogpost.subject }}</h1>
<h2 class="created-at">{{ blogpost.created_at }} <a href="/blog/{{ blogpost.data.name }}">permalink</a></h2> <h2 class="created-at">{{ blogpost.created_at }} <a href="/blog/{{ blogpost.data.name }}">permalink</a></h2>
<div class="tags">{% for tag in blogpost.tags %}<a class="tag" href="/tag/{{ tag }}">{{ tag }}</a>{% endfor %}</div> <div class="tags">{% for tag in blogpost.tags %}<a class="tag {% if filter_hashset %}{% if tag in filter_hashset %}active{% endif %}{% endif %}" href="/blog?filter={{ tag }}">{{ tag }}</a>{% endfor %}</div>
<div class="blogpost-content"> <div class="blogpost-content">
{{ blogpost.render }} {{ blogpost.render }}
</div> </div>

View File

@ -1,7 +1,7 @@
<div class="panel" id="filter-tags"> <div class="panel" id="filter-tags">
<h2>filter tag</h2> <h2>filter by tags</h2>
<div class="tags"> <div class="tags">
{% for tag in tags %}<a href="/blog/tag/{{ tag }}">{{ tag }}</a>{% endfor %} {% for tag in tags %}<a class="{% if tag in filter_hashset %}active{% endif %}" href="{% if tag in filter_hashset %}/blog{% else %}/blog?filter={{ tag }}{% endif %}">{{ tag }}</a>{% endfor %}
</div> </div>
<br> <br>
</div> </div>