A little app to serve my photography from my personal website

messin with tha UI

+85 -10
+21
assets/app.css
··· 1 + .photos { 2 + display: flex; 3 + flex-wrap: wrap; 4 + justify-content: center; 5 + gap: 20px; 6 + max-width: 1200px; 7 + } 8 + 9 + .photos > * { 10 + display: block; 11 + } 12 + 13 + .photos > * img { 14 + max-height: 300px; 15 + } 16 + 17 + .pagination { 18 + display: flex; 19 + list-style-type: none; 20 + gap: 10px; 21 + }
+7
src/db.rs
··· 60 60 pub tag: Option<String>, 61 61 } 62 62 63 + pub async fn get_photo_count(pool: &SqlitePool) -> anyhow::Result<u32> { 64 + let result: (u32,) = sqlx::query_as("SELECT COUNT(*) FROM photos") 65 + .fetch_one(pool) 66 + .await?; 67 + Ok(result.0) 68 + } 69 + 63 70 pub async fn get_photos(pool: &SqlitePool, pq: PhotoQuery) -> anyhow::Result<Vec<Photo>> { 64 71 let offset = pq.pagination.limit * (pq.pagination.page - 1); 65 72
+2
src/models.rs
··· 8 8 pub id: String, 9 9 pub caption: String, 10 10 pub filename: String, 11 + pub width: u32, 12 + pub height: u32, 11 13 #[sqlx(try_from = "String")] 12 14 pub taken_at: DateTime, 13 15 #[sqlx(try_from = "String")]
+22 -4
src/routes/photos.rs
··· 5 5 response::Html, 6 6 }; 7 7 use minijinja::context; 8 - use serde::{self, Deserialize}; 8 + use serde::{self, Deserialize, Serialize}; 9 9 10 10 use crate::{ 11 11 AppError, AppState, 12 - db::{self, Pagination, PhotoQuery, Sort, SortDirection, SortField}, 12 + db::{self, Pagination as QueryPagination, PhotoQuery, Sort, SortDirection, SortField}, 13 13 }; 14 + 15 + #[derive(Serialize)] 16 + pub struct Pagination { 17 + page: u32, 18 + num_pages: u32, 19 + } 14 20 15 21 #[derive(Deserialize)] 16 22 #[serde(default)] ··· 47 53 field: query.sort, 48 54 direction: query.dir, 49 55 }, 50 - pagination: Pagination { 56 + pagination: QueryPagination { 51 57 limit: query.limit, 52 58 page: query.page, 53 59 }, ··· 56 62 ) 57 63 .await?; 58 64 65 + let total_photos = db::get_photo_count(&state.pool).await?; 66 + 59 67 let current_tag = query.tag.clone(); 60 - let tags = db::get_tags(&state.pool).await?; 68 + let mut tags = db::get_tags(&state.pool).await?; 69 + if let Some(tag) = &query.tag { 70 + tags.retain(|t| t.name != *tag); 71 + } 72 + 61 73 let sort_dir = query.dir; 62 74 let sort_field = query.sort; 63 75 let template = state.template_env.get_template("photos/index")?; 76 + let pagination = Pagination { 77 + page: query.page, 78 + num_pages: total_photos / query.limit, 79 + }; 64 80 let rendered = template.render(context! { 65 81 photos => photos, 66 82 tags => tags, 83 + current_tag => current_tag, 67 84 current_tag => current_tag, 68 85 sort_dir => sort_dir, 69 86 sort_field => sort_field, 70 87 sort_fields => SORT_FIELDS, 88 + pagination => pagination, 71 89 })?; 72 90 73 91 Ok(Html(rendered))
+1
templates/layout.jinja
··· 2 2 <html> 3 3 <head> 4 4 <title>{% block title %}{% endblock %}</title> 5 + <link rel="stylesheet" href="/assets/app.css" /> 5 6 </head> 6 7 <body> 7 8 {% block body %}{% endblock %}
+32 -6
templates/photos/index.jinja
··· 4 4 <nav> 5 5 {{ dir }} 6 6 <ul> 7 + {% if current_tag %} 8 + <li>{{ current_tag }} <a href="/">🅇</a></li> 9 + {% endif %} 7 10 {% for tag in tags %} 8 11 <li> 9 12 {% if current_tag == tag.name -%} ··· 31 34 </select> 32 35 </select> 33 36 </nav> 34 - {% for photo in photos %} 35 - <a href="/{{ photo.id }}"> 36 - <p><time datetime="{{ photo.taken_at }}">{{ photo.taken_at }}</time></p> 37 - <img src="/thumbnails/{{ photo.id }}.webp"> 38 - </a> 39 - {% endfor %} 37 + <div class="photos"> 38 + {% for photo in photos %} 39 + <a href="/{{ photo.id }}"> 40 + <p><time datetime="{{ photo.taken_at }}">{{ photo.taken_at }}</time></p> 41 + <img 42 + style="aspect-ratio: {{ photo.width }} / {{ photo.height }}" 43 + src="/thumbnails/{{ photo.id }}.webp" 44 + > 45 + </a> 46 + {% endfor %} 47 + </div> 48 + 49 + <ul class="pagination"> 50 + {% if pagination.page > 1 %} 51 + <a href="?page={{ pagination.page - 1 }}">←</a> 52 + {% endif %} 53 + {% for i in range(1, pagination.num_pages+1) %} 54 + <li> 55 + {% if pagination.page == i %} 56 + {{ i }} 57 + {% else %} 58 + <a href="?page={{ i }}">{{ i }}</a> 59 + {% endif %} 60 + </li> 61 + {% endfor %} 62 + {% if pagination.page != pagination.num_pages %} 63 + <a href="?page={{ pagination.page + 1 }}">→</a> 64 + {% endif %} 65 + </ul> 40 66 {% endblock %}