blog post thumbnails
This commit is contained in:
parent
fdfa69c4b4
commit
9b81d70ae2
@ -5,6 +5,7 @@ segments:
|
|||||||
- blog
|
- blog
|
||||||
published: true
|
published: true
|
||||||
date: 2022-02-20T17:50:56.214Z
|
date: 2022-02-20T17:50:56.214Z
|
||||||
|
thumbnail: /images/uploads/laptopbattery.jpg
|
||||||
tags:
|
tags:
|
||||||
- News
|
- News
|
||||||
- Weekly
|
- Weekly
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- broadcasts
|
- broadcasts
|
||||||
published: true
|
published: true
|
||||||
date: 2022-04-26T20:22:21.191Z
|
date: 2022-04-26T20:22:21.191Z
|
||||||
|
thumbnail: /images/uploads/deeevbreak2.jpeg
|
||||||
tags:
|
tags:
|
||||||
- DevBreak
|
- DevBreak
|
||||||
---
|
---
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- broadcasts
|
- broadcasts
|
||||||
published: true
|
published: true
|
||||||
date: 2022-06-09T20:22:21.191Z
|
date: 2022-06-09T20:22:21.191Z
|
||||||
|
thumbnail: /images/uploads/deevbreakdoninik.jpeg
|
||||||
tags:
|
tags:
|
||||||
- DevBreak
|
- DevBreak
|
||||||
---
|
---
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
layout: blog
|
|
||||||
title: "Our attempt at Rusty game jam - Weekly #25-2022"
|
|
||||||
segments:
|
segments:
|
||||||
- blog
|
- blog
|
||||||
- featured
|
- featured
|
||||||
|
notes: ""
|
||||||
|
layout: blog
|
||||||
|
title: "Our attempt at Rusty game jam - Weekly #25-2022"
|
||||||
published: true
|
published: true
|
||||||
date: 2022-06-26T20:02:47.419Z
|
date: 2022-06-26T20:02:47.419Z
|
||||||
|
thumbnail: /images/uploads/screenshot-from-2022-06-26-22-37-16.png
|
||||||
tags:
|
tags:
|
||||||
- News
|
- News
|
||||||
- Weekly
|
- Weekly
|
||||||
- Development
|
- Development
|
||||||
notes: ""
|
|
||||||
---
|
---
|
||||||
Long time no see!
|
Long time no see!
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- blog
|
- blog
|
||||||
published: true
|
published: true
|
||||||
date: 2022-11-27T19:49:09.204Z
|
date: 2022-11-27T19:49:09.204Z
|
||||||
|
thumbnail: /images/uploads/image_7115.jpg
|
||||||
tags:
|
tags:
|
||||||
- News
|
- News
|
||||||
- Travel
|
- Travel
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- broadcasts
|
- broadcasts
|
||||||
published: true
|
published: true
|
||||||
date: 2023-01-21T20:22:21.191Z
|
date: 2023-01-21T20:22:21.191Z
|
||||||
|
thumbnail: /images/uploads/devbreak-4-3-.png
|
||||||
tags:
|
tags:
|
||||||
- DevBreak
|
- DevBreak
|
||||||
---
|
---
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- broadcasts
|
- broadcasts
|
||||||
published: true
|
published: true
|
||||||
date: 2023-02-04T20:22:21.191Z
|
date: 2023-02-04T20:22:21.191Z
|
||||||
|
thumbnail: /images/uploads/devbreak.jpeg
|
||||||
tags:
|
tags:
|
||||||
- DevBreak
|
- DevBreak
|
||||||
---
|
---
|
||||||
|
@ -6,6 +6,7 @@ segments:
|
|||||||
- featured
|
- featured
|
||||||
published: true
|
published: true
|
||||||
date: 2023-04-27T21:22:21.191Z
|
date: 2023-04-27T21:22:21.191Z
|
||||||
|
thumbnail: /images/uploads/teta.png
|
||||||
tags:
|
tags:
|
||||||
- Presentation
|
- Presentation
|
||||||
- Keyboards
|
- Keyboards
|
||||||
|
@ -5,6 +5,7 @@ segments:
|
|||||||
- blog
|
- blog
|
||||||
published: true
|
published: true
|
||||||
date: 2023-06-24T16:34:45.527Z
|
date: 2023-06-24T16:34:45.527Z
|
||||||
|
thumbnail: /images/uploads/img_9715-rotated.jpg
|
||||||
tags:
|
tags:
|
||||||
- News
|
- News
|
||||||
- Personal
|
- Personal
|
||||||
|
@ -6,6 +6,7 @@ segments:
|
|||||||
- featured
|
- featured
|
||||||
published: true
|
published: true
|
||||||
date: 2023-08-29T19:34:17.071Z
|
date: 2023-08-29T19:34:17.071Z
|
||||||
|
thumbnail: /images/uploads/20230226_130037.jpg
|
||||||
tags:
|
tags:
|
||||||
- News
|
- News
|
||||||
- Keyboards
|
- Keyboards
|
||||||
|
@ -14,6 +14,10 @@ layout {
|
|||||||
pane {
|
pane {
|
||||||
command "just"
|
command "just"
|
||||||
args "tailwind"
|
args "tailwind"
|
||||||
|
}
|
||||||
|
pane {
|
||||||
|
command "just"
|
||||||
|
args "decap_server"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pane size=2 borderless=true {
|
pane size=2 borderless=true {
|
||||||
|
@ -30,3 +30,6 @@ opt-level = 1
|
|||||||
lto = "thin"
|
lto = "thin"
|
||||||
panic = "unwind"
|
panic = "unwind"
|
||||||
strip = false
|
strip = false
|
||||||
|
|
||||||
|
[profile.dev.package.askama_derive]
|
||||||
|
opt-level = 3
|
||||||
|
@ -7,7 +7,6 @@ pub const BLOG_POST_PATH: &str = "../_posts/blog";
|
|||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct BlogPostMetadata {
|
pub struct BlogPostMetadata {
|
||||||
pub layout: String,
|
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub segments: Vec<String>,
|
pub segments: Vec<String>,
|
||||||
pub published: bool,
|
pub published: bool,
|
||||||
|
@ -5,12 +5,11 @@ use crate::post_utils::{post_listing::get_post_list, post_parser::ParseResult};
|
|||||||
use super::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH};
|
use super::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH};
|
||||||
|
|
||||||
pub async fn get_featured_blog_posts() -> Result<Vec<ParseResult<BlogPostMetadata>>, StatusCode> {
|
pub async fn get_featured_blog_posts() -> Result<Vec<ParseResult<BlogPostMetadata>>, StatusCode> {
|
||||||
let post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;
|
let mut post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;
|
||||||
|
post_list.retain(|post| post.metadata.segments.contains(&"featured".to_string()));
|
||||||
|
post_list.retain(|post| post.metadata.published);
|
||||||
|
post_list.sort_by_key(|post| post.metadata.date);
|
||||||
|
post_list.reverse();
|
||||||
|
|
||||||
let featured_posts = post_list
|
Ok(post_list)
|
||||||
.into_iter()
|
|
||||||
.filter(|post| post.metadata.segments.contains(&"featured".to_string()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(featured_posts)
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ mod components;
|
|||||||
mod feed;
|
mod feed;
|
||||||
mod filters;
|
mod filters;
|
||||||
mod pages;
|
mod pages;
|
||||||
|
mod picture_generator;
|
||||||
mod post_utils;
|
mod post_utils;
|
||||||
mod projects;
|
mod projects;
|
||||||
mod router;
|
mod router;
|
||||||
|
@ -30,6 +30,7 @@ pub async fn render_blog_post_list(
|
|||||||
let site_footer = render_site_footer().await?;
|
let site_footer = render_site_footer().await?;
|
||||||
let mut post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;
|
let mut post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;
|
||||||
post_list.sort_by_key(|post| post.metadata.date);
|
post_list.sort_by_key(|post| post.metadata.date);
|
||||||
|
post_list.retain(|post| post.metadata.published);
|
||||||
post_list.reverse();
|
post_list.reverse();
|
||||||
|
|
||||||
let posts = match &tag {
|
let posts = match &tag {
|
||||||
|
24
axum_server/src/picture_generator/mod.rs
Normal file
24
axum_server/src/picture_generator/mod.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
This is going to be an attempt for creating HTML markup for serving and generating images
|
||||||
|
for the most common PIXEL_DENSITIES.
|
||||||
|
It should create `<picture>` elements with following features:
|
||||||
|
|
||||||
|
- least amount of needed arguments
|
||||||
|
- for each pixel density it should have a definition in `srcset`
|
||||||
|
- create a `avif` type for the image for each pixel_density
|
||||||
|
- create an image in the original format for each pixel_density
|
||||||
|
- support case of `svg` therefore not doing any of the pixel_density logic
|
||||||
|
|
||||||
|
These features might be considered later:
|
||||||
|
- support case for art direction (different pictures for different screen sizes)
|
||||||
|
|
||||||
|
|
||||||
|
TODO: figure wether `height` or `width` have to be known ahead of time
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
It can be used from the rust code
|
||||||
|
It should be used from the templates as well
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub mod picture_markup_generator;
|
101
axum_server/src/picture_generator/picture_markup_generator.rs
Normal file
101
axum_server/src/picture_generator/picture_markup_generator.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
|
pub const PIXEL_DENSITIES: [f64; 5] = [1., 1.5, 2., 3., 4.];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ExportFormat {
|
||||||
|
JPG,
|
||||||
|
AVIF,
|
||||||
|
SVG,
|
||||||
|
PNG,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_picture_markup(orig_img_path: &str, width: u32, height: u32) -> String {
|
||||||
|
let exported_formats = get_export_formats(&orig_img_path);
|
||||||
|
|
||||||
|
for export_format in exported_formats {
|
||||||
|
|
||||||
|
// TODO get original img resolution and determine how many exports are going to be needed
|
||||||
|
// let orig_img_resolution =
|
||||||
|
// let resolutions = get_resolutions(width, height);
|
||||||
|
|
||||||
|
let resolutions = vec![(300, 200), (450, 300), (600, 400), (900, 600), (1200, 800)];
|
||||||
|
let generated_paths = get_generated_paths(orig_img_path, resolutions);
|
||||||
|
|
||||||
|
}
|
||||||
|
let result = format!("<picture></picture>");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_generated_paths(orig_img_path: &str, &resolutions: Vec<(i32, i32)>) -> Vec<String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_generated_paths {
|
||||||
|
let orig_img_path = "./images/uploads/img_name.jpg";
|
||||||
|
let resolutions = vec![(300, 200), (450, 300), (600, 400), (900, 600), (1200, 800)];
|
||||||
|
assert_eq!(get_generated_paths(orig_img_path,&resolutions), vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO get original img resolution and determine how many exports are going to be needed
|
||||||
|
fn get_resolutions(orig_width: i32, orig_height: i32, width: i32, height: i32) -> Vec<(i32, i32)> {
|
||||||
|
todo!("get original img resolution and determine how many exports are going to be needed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_srcset(new_path: &str, width: u32, height: u32) -> &str {
|
||||||
|
todo!("generate srcset")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_export_formats(orig_img_path: &str) -> Vec<ExportFormat> {
|
||||||
|
let path = Path::new(&orig_img_path).extension().and_then(|ext| ext.to_str());
|
||||||
|
|
||||||
|
match path {
|
||||||
|
Some("jpg" | "jpeg") => vec![ExportFormat::AVIF, ExportFormat::JPG]
|
||||||
|
Some("png") => vec![ExportFormat::AVIF, ExportFormat::PNG]
|
||||||
|
Some(_) | None => vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_export_formats {
|
||||||
|
assert_eq!(get_export_formats("./images/uploads/img_name.jpg"), vec![ExportFormat::AVIF, ExportFormat::JPG])
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_generate_srcset() {
|
||||||
|
let width = 400;
|
||||||
|
let height = 200;
|
||||||
|
let orig_img_path = "./images/uploads/img_name.jpg";
|
||||||
|
let result = "./generated_images/images/uploads/img_name_400x200.avif 1x, ./generated_images/images/uploads/img_name_500x300.avif 1.5x, ./generated_images/images/uploads/img_name_800x400.avif 2x, ./generated_images/images/uploads/img_name_1200x600.avif 3x, ./generated_images/images/uploads/img_name_1600x800.avif 4x";
|
||||||
|
assert_eq!(generate_srcset(orig_img_path, width, height), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_picture_markup() {
|
||||||
|
let width = 300;
|
||||||
|
let height = 200;
|
||||||
|
let orig_img_path = "./images/uploads/img_name.jpg";
|
||||||
|
let result = r#"""
|
||||||
|
<picture>
|
||||||
|
<source
|
||||||
|
srcset="./generated_images/images/uploads/img_name_300x200.avif 1x, ./generated_images/images/uploads/img_name_450x300.avif 1.5x, ./generated_images/images/uploads/img_name_600x400.avif 2x, ./generated_images/images/uploads/img_name_900x600.avif 3x, ./generated_images/images/uploads/img_name_1200x800.avif 4x"
|
||||||
|
type="image/avif"
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
srcset="./generated_images/images/uploads/img_name_300x200.jpg 1x, ./generated_images/images/uploads/img_name_450x300.jpg 1.5x, ./generated_images/images/uploads/img_name_600x400.jpg 2x, ./generated_images/images/uploads/img_name_900x600.jpg 3x, ./generated_images/images/uploads/img_name_1200x800.jpg 4x"
|
||||||
|
type="image/jpeg"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="./generated_images/images/uploads/img_name_300x200.jpg"
|
||||||
|
width="300"
|
||||||
|
height="200"
|
||||||
|
>
|
||||||
|
</picture>
|
||||||
|
"""#;
|
||||||
|
assert_eq!(
|
||||||
|
generate_picture_markup(orig_img_path, width, height),
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
@ -30,11 +30,8 @@ pub async fn get_post_list<'de, Metadata: DeserializeOwned>(
|
|||||||
.unwrap_or_else(|_| "DEV".to_owned())
|
.unwrap_or_else(|_| "DEV".to_owned())
|
||||||
.eq("PROD")
|
.eq("PROD")
|
||||||
{
|
{
|
||||||
posts = posts
|
posts.retain(|post| !post.slug.starts_with("dev"))
|
||||||
.into_iter()
|
|
||||||
.filter(|post| !post.slug.starts_with("dev"))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(posts);
|
Ok(posts)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
! tailwindcss v3.4.7 | MIT License | https://tailwindcss.com
|
! tailwindcss v3.4.9 | MIT License | https://tailwindcss.com
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -672,10 +672,6 @@ video {
|
|||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aspect-\[4\/3\] {
|
|
||||||
aspect-ratio: 4/3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-0 {
|
.h-0 {
|
||||||
height: 0px;
|
height: 0px;
|
||||||
}
|
}
|
||||||
@ -832,11 +828,6 @@ video {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-cover {
|
|
||||||
-o-object-fit: cover;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-0 {
|
.p-0 {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<article class="grid grid-cols-[1fr_2fr] grid-flow-col gap-4">
|
<article class="grid grid-cols-[1fr_2fr] grid-flow-col gap-4">
|
||||||
<aside class="row-span-3">
|
<aside class="row-span-3">
|
||||||
<!-- TODO <figure> -->
|
<!-- TODO <figure> -->
|
||||||
<svg aria-hidden="true" class="h-12 w-12 fill-blue-950">
|
<!-- TODO Thumbnail -->
|
||||||
<use xlink:href="/svg/icons-sprite.svg#mail" />
|
<!-- <svg aria-hidden="true" class="h-12 w-12 fill-blue-950"> -->
|
||||||
</svg>
|
<!-- <use xlink:href="/svg/icons-sprite.svg#mail" /> -->
|
||||||
|
<!-- </svg> -->
|
||||||
|
<figure>
|
||||||
</aside>
|
</aside>
|
||||||
<header>
|
<header>
|
||||||
<h3 class="text-lg font-bold mb-1">{{post.metadata.title}}</h3>
|
<h3 class="text-lg font-bold mb-1">{{post.metadata.title}}</h3>
|
@ -54,7 +54,7 @@
|
|||||||
<ul class="mx-5">
|
<ul class="mx-5">
|
||||||
{% for post in featured_blog_posts %}
|
{% for post in featured_blog_posts %}
|
||||||
<li>
|
<li>
|
||||||
{% include "components/post_preview.html" %}
|
{% include "components/blog_post_preview.html" %}
|
||||||
<hr class="border-blue-950 my-5">
|
<hr class="border-blue-950 my-5">
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
BIN
static/images/uploads/deeevbreak2.jpeg
(Stored with Git LFS)
Normal file
BIN
static/images/uploads/deeevbreak2.jpeg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/uploads/deevbreakdoninik.jpeg
(Stored with Git LFS)
Normal file
BIN
static/images/uploads/deevbreakdoninik.jpeg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/uploads/devbreak-4-3-.png
(Stored with Git LFS)
Normal file
BIN
static/images/uploads/devbreak-4-3-.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/uploads/devbreak.jpeg
(Stored with Git LFS)
Normal file
BIN
static/images/uploads/devbreak.jpeg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/uploads/teta.png
(Stored with Git LFS)
Normal file
BIN
static/images/uploads/teta.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -17,7 +17,6 @@ collections:
|
|||||||
create: true # Allow users to create new documents in this collection
|
create: true # Allow users to create new documents in this collection
|
||||||
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
|
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
|
||||||
fields: # The fields for each document, usually in front matter
|
fields: # The fields for each document, usually in front matter
|
||||||
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
|
|
||||||
- { label: 'Title', name: 'title', widget: 'string' }
|
- { label: 'Title', name: 'title', widget: 'string' }
|
||||||
- label: 'Segments'
|
- label: 'Segments'
|
||||||
name: 'segments'
|
name: 'segments'
|
||||||
|
Loading…
Reference in New Issue
Block a user