src/server/hx/gallery.rs
Rama main · réplica de lectura del repositorio original.
use crate::db;
use crate::server::AppState;
use chrono::Datelike;
use super::Cursor;
use super::GalleryState;
use super::Media;
use super::render::RenderResult;
use super::render::ServerError;
use super::render::TemplatedResponse;
use askama::Template;
use axum::extract::{Query, State};
use axum::{Router, routing::get};
const PAGE_SIZE: usize = 400;
const YEARS: usize = 14;
mod filters {
#![allow(clippy::inline_always, clippy::unused_self)]
use core::fmt;
use super::super::{Cursor, Media, gallery::PAGE_SIZE};
const EMPTY: String = String::new();
#[askama::filter_fn]
pub fn cursor(media: &[Media], _: &dyn askama::Values) -> askama::Result<String> {
if media.len() < PAGE_SIZE {
Ok(EMPTY)
} else {
Ok(serde_urlencoded::to_string(Cursor {
cursor: media.last().expect("List should not be empty").taken_at,
})
.map_err(|_| fmt::Error)?)
}
}
}
#[derive(Debug)]
enum BarButton {
Favorite(bool),
Year(bool, String),
Month(bool, u8, String),
Order(bool),
Collection(bool),
Clear,
Empty,
}
#[derive(Template)]
#[template(path = "web/gallery/gallery.html")]
struct HxGallery<'a> {
bar_buttons: &'a Vec<BarButton>,
features: &'a crate::server::Features,
state: &'a GalleryState,
media: &'a Vec<Media>,
oob: bool,
}
pub(super) async fn render(app_state: &AppState, state: GalleryState) -> RenderResult {
let now = chrono::Utc::now();
let pool = &app_state.pool;
let config = &app_state.config;
#[allow(clippy::cast_sign_loss)]
let current_year = now.year() as usize;
#[allow(clippy::cast_possible_truncation)]
let current_month = now.month() as u8;
let new_to_old = state.new_to_old.unwrap_or(true);
let mut buttons = Vec::with_capacity(18);
if state.collection.is_none() {
if state.year.is_none() {
for i in (current_year - YEARS..=current_year).rev() {
buttons.push(BarButton::Year(false, format!("{i}")));
}
} else if let Some(year) = state.year {
if year == current_year {
buttons.push(BarButton::Empty);
} else {
buttons.push(BarButton::Year(false, format!("{}", year + 1)));
}
if new_to_old {
buttons.push(BarButton::Year(true, format!("{year}")));
}
for (month_nr, month_text) in vec![
(12, "Dec"),
(11, "Nov"),
(10, "Oct"),
(9, "Sep"),
(8, "Aug"),
(7, "Jul"),
(6, "Jun"),
(5, "May"),
(4, "Apr"),
(3, "Mar"),
(2, "Feb"),
(1, "Jan"),
] {
if year == current_year && month_nr > current_month {
buttons.push(BarButton::Empty);
} else {
buttons.push(BarButton::Month(
Some(month_nr) == state.month,
month_nr,
(*month_text).to_string(),
));
}
}
if !new_to_old {
buttons.push(BarButton::Year(true, format!("{year}")));
}
buttons.push(BarButton::Year(false, format!("{}", year - 1)));
}
}
if !new_to_old {
buttons = buttons.into_iter().rev().collect();
}
if app_state.config.features.favorite_allow {
buttons.insert(0, BarButton::Favorite(state.favorite.unwrap_or(false)));
}
buttons.push(BarButton::Order(new_to_old));
match (state.favorite, state.year) {
(Some(true), _) | (_, Some(_)) => buttons.push(BarButton::Clear),
(_, _) => buttons.push(BarButton::Empty),
}
buttons.push(BarButton::Collection(state.collection.is_some()));
let media = db::media_list(pool, &state, &state, PAGE_SIZE)
.await
.map_err(ServerError::DB)?
.into_iter()
.map(|row| (row, &config.pathfinder).into())
.collect();
HxGallery {
bar_buttons: &buttons,
features: &config.features,
state: &state,
media: &media,
oob: false,
}
.render_response()
}
async fn root(
State(app_state): State<AppState>,
Query(state): Query<GalleryState>,
) -> RenderResult {
render(&app_state, state).await
}
#[derive(Template)]
#[template(path = "web/gallery/list.html")]
struct HxMore<'a> {
features: &'a crate::server::Features,
media: &'a Vec<Media>,
oob: bool,
}
async fn more(
State(app_state): State<AppState>,
Query(state): Query<GalleryState>,
Query(cursor): Query<Cursor>,
) -> RenderResult {
let pool = &app_state.pool;
let config = &app_state.config;
let media = db::media_list(pool, (&state, &cursor), &state, PAGE_SIZE)
.await
.map_err(ServerError::DB)?
.into_iter()
.map(|row| (row, &config.pathfinder).into())
.collect();
HxMore {
features: &config.features,
media: &media,
oob: false,
}
.render_response()
}
pub fn create_router() -> Router<AppState> {
Router::new()
.route("/", get(root))
.route("/more", get(more))
}