implement sorting

This commit is contained in:
slonkazoid 2024-08-01 00:25:42 +03:00
parent 02216efb7d
commit 00b721caab
Signed by: slonk
SSH key fingerprint: SHA256:tbZfJX4IOvZ0LGWOWu5Ijo8jfMPi78TU7x1VoEeCIjM
9 changed files with 105 additions and 30 deletions

View file

@ -43,11 +43,12 @@ title = "bingus-blog" # title of the blog
description = "blazingly fast markdown blog software written in rust memory safe" description = "blazingly fast markdown blog software written in rust memory safe"
markdown_access = true # allow users to see the raw markdown of a post markdown_access = true # allow users to see the raw markdown of a post
# endpoint: /posts/<name>.md # endpoint: /posts/<name>.md
js_enable = true # enable javascript (required for below 2 options)
date_format = "RFC3339" # format string used to format dates in the backend date_format = "RFC3339" # format string used to format dates in the backend
# it's highly recommended to leave this as default, # it's highly recommended to leave this as default,
# so the date can be formatted by the browser. # so the date can be formatted by the browser.
# format: https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers # format: https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers
js_enable = true # enable javascript (required for above) default_sort = "date" # default sorting method ("date" or "name")
default_color = "#f5c2e7" # default embed color, optional default_color = "#f5c2e7" # default embed color, optional
[rss] [rss]
@ -144,7 +145,7 @@ created_at: 2024-04-18T04:15:26+03:00 # date of writing, this is highly
# because this is fetched from file stats by default # because this is fetched from file stats by default
#modified_at: ... # see above. this is also fetched from the filesystem #modified_at: ... # see above. this is also fetched from the filesystem
tags: # tags, or keywords, used in meta and also in the ui tags: # tags, or keywords, used in meta and also in the ui
- lifestyle - lifestyle
--- ---
``` ```
@ -165,11 +166,11 @@ standard. examples of valid and invalid dates:
## Routes ## Routes
- `GET /`: index page, lists posts - `GET /`: index page, lists posts
- `GET /posts`: returns a list of all posts with metadata in JSON format - `GET /posts`: returns a list of all posts with metadata in JSON format
- `GET /posts/<name>`: view a post - `GET /posts/<name>`: view a post
- `GET /posts/<name>.md`: view the raw markdown of a post - `GET /posts/<name>.md`: view the raw markdown of a post
- `GET /post/*`: redirects to `/posts/*` - `GET /post/*`: redirects to `/posts/*`
## Cache ## Cache
@ -190,8 +191,8 @@ there is basically no good reason to not have compression on.
make sure your changes don't cause problems on at least the following browsers: make sure your changes don't cause problems on at least the following browsers:
- links2 - links2
- lynx - lynx
- w3m - w3m
- Firefox - Firefox
- Chromium - Chromium

View file

@ -14,7 +14,7 @@ use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing::{info, info_span, Span}; use tracing::{info, info_span, Span};
use crate::config::{Config, DateFormat}; use crate::config::{Config, DateFormat, Sort};
use crate::error::{AppError, AppResult}; use crate::error::{AppError, AppResult};
use crate::filters; use crate::filters;
use crate::post::{MarkdownPosts, PostManager, PostMetadata, RenderStats, ReturnedPost}; use crate::post::{MarkdownPosts, PostManager, PostMetadata, RenderStats, ReturnedPost};
@ -35,6 +35,7 @@ struct IndexTemplate<'a> {
df: &'a DateFormat, df: &'a DateFormat,
js: bool, js: bool,
color: Option<&'a str>, color: Option<&'a str>,
sort: Sort,
} }
#[derive(Template)] #[derive(Template)]
@ -72,6 +73,7 @@ async fn index<'a>(
df: &config.date_format, df: &config.date_format,
js: config.js_enable, js: config.js_enable,
color: config.default_color.as_deref(), color: config.default_color.as_deref(),
sort: config.default_sort,
} }
.into_response()) .into_response())
} }

View file

@ -67,14 +67,24 @@ pub enum DateFormat {
Strftime(String), Strftime(String),
} }
#[derive(Serialize, Deserialize, Debug, Clone, Default, Copy, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum Sort {
#[default]
Date,
Name,
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(default)] #[serde(default)]
pub struct Config { pub struct Config {
pub title: String, pub title: String,
pub description: String, pub description: String,
pub markdown_access: bool, pub markdown_access: bool,
pub date_format: DateFormat,
pub js_enable: bool, pub js_enable: bool,
pub date_format: DateFormat,
pub default_sort: Sort,
pub default_color: Option<String>, pub default_color: Option<String>,
pub rss: RssConfig, pub rss: RssConfig,
pub dirs: DirsConfig, pub dirs: DirsConfig,
@ -89,8 +99,9 @@ impl Default for Config {
title: "bingus-blog".into(), title: "bingus-blog".into(),
description: "blazingly fast markdown blog software written in rust memory safe".into(), description: "blazingly fast markdown blog software written in rust memory safe".into(),
markdown_access: true, markdown_access: true,
date_format: Default::default(),
js_enable: true, js_enable: true,
date_format: Default::default(),
default_sort: Default::default(),
default_color: Some("#f5c2e7".into()), default_color: Some("#f5c2e7".into()),
// i have a love-hate relationship with serde // i have a love-hate relationship with serde
// it was engimatic at first, but then i started actually using it // it was engimatic at first, but then i started actually using it

View file

@ -50,3 +50,7 @@ pub fn collect_tags(posts: &Vec<PostMetadata>) -> Result<Vec<(String, u64)>, ask
Ok(tags) Ok(tags)
} }
pub fn join_tags_for_meta(tags: &Vec<String>) -> Result<String, askama::Error> {
Ok(tags.join(", "))
}

4
static/date.js Normal file
View file

@ -0,0 +1,4 @@
for (let el of document.querySelectorAll(".date-rfc3339")) {
let date = new Date(Date.parse(el.textContent));
el.textContent = date.toLocaleString();
}

View file

@ -1,4 +1,40 @@
for (let el of document.querySelectorAll(".date-rfc3339")) { let form = document.getElementById("sort");
let date = new Date(Date.parse(el.textContent)); let posts = document.getElementById("posts");
el.textContent = date.toLocaleString();
let postsName = document.createElement("div");
function initialSort(source, target) {
let posts = [];
for (let post of source.children) {
let title = post.firstElementChild.innerText;
posts.push([title, post.cloneNode(true)]);
}
posts.sort(([a, _1], [b, _2]) => a.toLocaleLowerCase() > b.toLocaleLowerCase());
for (let [_, post] of posts) {
target.appendChild(post);
}
} }
function sort(by) {
console.log("sorting by", by);
switch (by) {
case "date":
posts.style.display = "block";
postsName.style.display = "none";
break;
case "name":
postsName.style.display = "block";
posts.style.display = "none";
break;
}
}
function handleSort() {
if (!form) return;
for (let el of form.sort) el.addEventListener("change", () => sort(form.sort.value));
}
initialSort(posts, postsName);
posts.parentNode.appendChild(postsName);
handleSort();
sort(form.sort.value);

View file

@ -112,6 +112,11 @@ div.post {
grid-row: 3; grid-row: 3;
} }
#sort {
display: inline-block;
margin-bottom: 1rem;
}
/* BEGIN cool effect everyone liked */ /* BEGIN cool effect everyone liked */
body { body {

View file

@ -17,6 +17,7 @@
{% endif %} {% endif %}
<!-- prettier-br --> <!-- prettier-br -->
{% if js %} {% if js %}
<script src="/static/date.js" defer></script>
<script src="/static/main.js" defer></script> <script src="/static/main.js" defer></script>
{% endif %} {% endif %}
</head> </head>
@ -25,20 +26,31 @@
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<p>{{ description }}</p> <p>{{ description }}</p>
<h2>posts</h2> <h2>posts</h2>
<!-- prettier-ignore -->
<div> <div>
{% if posts.is_empty() %} {% if posts.is_empty() %}<!-- prettier-br -->
there are no posts right now. check back later! there are no posts right now. check back later!<!-- prettier-br -->
{% else %}<!-- prettier-br -->
{% if js %}<!-- prettier-br -->
sort by:
<form id="sort">
<input type="radio" name="sort" id="sort-date" value="date" {% if sort == Sort::Date %} checked {% endif %} />
<label for="sort-date">date</label>
<input type="radio" name="sort" id="sort-name" value="name" {% if sort == Sort::Name %} checked {% endif %} />
<label for="sort-name">name</label>
</form>
{% endif %}<!-- prettier-br --> {% endif %}<!-- prettier-br -->
{% for post in posts %} <div id="posts">
<div class="post"> {% for post in posts %}
<a href="/posts/{{ post.name }}"><b>{{ post.title }}</b></a> <div class="post">
<span class="post-author">- by {{ post.author }}</span> <a href="/posts/{{ post.name }}"><b>{{ post.title }}</b></a>
<br /> <span class="post-author">- by {{ post.author }}</span>
{{ post.description }}<br /> <br />
{% call macros::table(post) %} {{ post.description }}<br />
{% call macros::table(post) %}
</div>
{% endfor %}
</div> </div>
{% endfor %} {% endif %}<!-- prettier-br -->
</div> </div>
{% let tags = posts|collect_tags %}<!-- prettier-br --> {% let tags = posts|collect_tags %}<!-- prettier-br -->
{% if !tags.is_empty() %} {% if !tags.is_empty() %}

View file

@ -5,7 +5,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="author" content="{{ meta.author }}" /> <meta name="author" content="{{ meta.author }}" />
<meta name="keywords" content="{{ meta.tags|join(", ") }}" /> <meta name="keywords" content="{{ meta.tags|join_tags_for_meta }}" />
<meta name="description" content="{{ meta.title }}" /> <meta name="description" content="{{ meta.title }}" />
<!-- you know what I really love? platforms like discord <!-- you know what I really love? platforms like discord
favoring twitter embeds over the open standard. to color favoring twitter embeds over the open standard. to color