Projects moved and finished to showcase on index

This commit is contained in:
2024-08-06 22:45:30 +02:00
parent 8b6dbc83c7
commit 6dc6a581e3
37 changed files with 482 additions and 90 deletions

View File

@ -1,4 +1,8 @@
use crate::{pages::project::ProjectMetadata, post_list::get_post_list, post_parser::ParseResult};
use crate::{
pages::project::ProjectMetadata,
post_list::get_post_list,
post_parser::{parse_html, ParseResult},
};
use axum::http::StatusCode;
pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>, StatusCode> {
@ -7,6 +11,10 @@ pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>
let featured_projects = project_list
.into_iter()
.filter(|post| post.metadata.featured)
.map(|mut post| {
post.metadata.description = parse_html(&post.metadata.description);
post
})
.collect();
Ok(featured_projects)

View File

@ -9,4 +9,15 @@ pub struct ProjectMetadata {
pub cover_image: Option<String>,
pub tags: Vec<String>,
pub featured: bool,
pub link: Option<String>,
}
pub fn translate_classification(classification: &str) -> &str {
match classification {
"webapp" => "Web application",
"website" => "Web site",
"presentation" => "Presentation",
"videogame" => "Video game",
any => any,
}
}

View File

@ -40,7 +40,7 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
.parse_with_struct::<Metadata>(&file_contents)
.ok_or_else(|| {
tracing::error!("Failed to parse metadata");
return StatusCode::INTERNAL_SERVER_ERROR;
StatusCode::INTERNAL_SERVER_ERROR
})?;
let body = parse_html(&metadata.content);
@ -52,14 +52,14 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
.to_owned();
return Ok(ParseResult {
Ok(ParseResult {
body,
metadata: metadata.data,
slug: filename,
});
})
}
fn parse_html(markdown: &str) -> String {
pub fn parse_html(markdown: &str) -> String {
let mut options = Options::empty();
options.insert(Options::ENABLE_TABLES);
options.insert(Options::ENABLE_FOOTNOTES);
@ -68,45 +68,45 @@ fn parse_html(markdown: &str) -> String {
options.insert(Options::ENABLE_SMART_PUNCTUATION);
options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
let parser = Parser::new_ext(&markdown, options).map(|event| match event {
Event::Start(ref tag) => match tag {
// Parsing images considers `alt` attribute as inner `Text` event
// Therefore the `[alt]` is rendered in html as subtitle
// and the `[](url "title")` `title` is rendered as `alt` attribute
Tag::Image {
link_type,
dest_url,
title,
id,
} => {
println!(
"Image link_type: {:?} url: {} title: {} id: {}",
link_type, dest_url, title, id
);
// TODO src set
Event::Html(
format!(
r#"<figure>
let parser = Parser::new_ext(markdown, options).map(|event| match event {
/*
Parsing images considers `alt` attribute as inner `Text` event
Therefore the `[alt]` is rendered in html as subtitle
and the `[](url "title")` `title` is rendered as `alt` attribute
*/
Event::Start(Tag::Image {
link_type,
dest_url,
title,
id,
}) => {
println!(
"Image link_type: {:?} url: {} title: {} id: {}",
link_type, dest_url, title, id
);
// TODO src set
Event::Html(
format!(
r#"<figure>
<img
alt="{alt}"
src="{src}"
/>
<figcaption>
"#,
alt = title,
src = dest_url,
)
.into(),
alt = title,
src = dest_url,
)
}
_ => event,
},
.into(),
)
}
Event::Start(_) => event,
Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
_ => event,
});
// Write to String buffer.
// Write to String buffer
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, parser);
return html;
html
}

View File

@ -1,42 +0,0 @@
use std::path::Path;
use axum::http::StatusCode;
use serde::de::DeserializeOwned;
use tokio::fs::read_dir;
use tracing::info;
use crate::post_parser::{parse_post, ParseResult};
pub async fn get_post_list<'de, Metadata: DeserializeOwned>(
path: &Path,
) -> Result<Vec<ParseResult<Metadata>>, StatusCode> {
// let path = "../_posts/blog/";
let mut dir = read_dir(path)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let mut posts: Vec<ParseResult<Metadata>> = Vec::new();
while let Some(file) = dir
.next_entry()
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
{
let file_path = file.path();
let file_path_str = file_path.to_str().unwrap();
info!(":{}", file_path_str);
let post = parse_post::<Metadata>(file_path_str).await?;
posts.push(post);
}
if std::env::var("TARGET")
.unwrap_or_else(|_| "DEV".to_owned())
.eq("PROD")
{
posts = posts
.into_iter()
.filter(|post| !post.slug.starts_with("dev"))
.collect()
}
return Ok(posts);
}

View File

@ -624,6 +624,16 @@ video {
margin-bottom: 1.5rem;
}
.mx-1 {
margin-left: 0.25rem;
margin-right: 0.25rem;
}
.my-1 {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
@ -644,6 +654,10 @@ video {
margin-top: 0.75rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.block {
display: block;
}
@ -892,6 +906,11 @@ video {
line-height: 1.25rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.font-bold {
font-weight: 700;
}

View File

@ -1,34 +1,45 @@
<article>
<article class="border rounded-md bg-white m-4 p-4">
<header class="px-4 mb-3">
<h2 class="text-3xl font-semibold text-blue-900">
{{project.metadata.title}}
<h2 class="text-xl font-semibold text-blue-900">
{% match project.metadata.link %}
{% when Some with (href) %}
<a href="{{href}}" class="text-blue-900 no-underline">
{{project.metadata.title}}
</a>
{% when None %}
{{project.metadata.title}}
{% endmatch %}
</h2>
<p class="px-5 text-gray-800">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make</p>
<p class="text-gray-800 text-lg my-2">
{{project.metadata.description|safe}}
</p>
</header>
<!-- <hr class="border-blue-950 my-5"> -->
{% match project.metadata.cover_image %}
{% when Some with (source) %}
<figure>
<!-- <img src="{{source}}" /> -->
<figure class="my-2">
{% match project.metadata.link %}
{% when Some with (href) %}
<a href="{{href}}">
<img src="{{source}}" />
</a>
{% when None %}
<img src="{{source}}" />
{% endmatch %}
<!-- TODO <figure> -->
<svg aria-hidden="true" class="h-12 w-12 fill-blue-950">
<use xlink:href="/svg/icons-sprite.svg#mail" />
</svg>
</figure>
{% when None %}
{% endmatch %}
<footer class="text-sm">
<h3 class="text-3xl font-semibold text-blue-900">
TODO classification
<footer class="text-sm px-4">
<h3 class="text-xl font-semibold text-blue-900 my-2">
{{crate::pages::project::translate_classification(project.metadata.classification)}}
</h3>
<ul class="inline-block">
{% for tag in project.metadata.tags %}
<li class="inline-block">
<li class="inline-block text-blue-700 italic">
{{tag}}
</li>
{% endfor %}