diff --git a/src/server/events.rs b/src/server/events.rs index d93cda2..d23d539 100644 --- a/src/server/events.rs +++ b/src/server/events.rs @@ -20,5 +20,5 @@ async fn post_event( let mut context = Context::new(); context.insert("event", &event); - Ok(Html(Update::EventLi.render_ctx(&ctx.tera, &context)?)) + Ok(Html(Update::EventLi.render(&ctx.tera, &context)?)) } diff --git a/src/server/images.rs b/src/server/images.rs index 2009dbd..bd9203b 100644 --- a/src/server/images.rs +++ b/src/server/images.rs @@ -20,7 +20,7 @@ use std::{io::BufWriter, path::PathBuf}; use super::error::AppError; use crate::{ db::{self, Image, NewImage, Pagination}, - template::{List, ListCard, Template, View}, + template::{Template, View}, IMG_DIR, }; @@ -72,11 +72,15 @@ async fn get_images( .await .unwrap()?; - let list = List::Card - .items("Images", ListCard::Image, images) - .page("/images", page) - .query(filter); - Ok(Html(View::other(list).headers(&headers).render(&ctx.tera)?)) + let mut context = Context::new(); + context.insert("images", &images); + + let is_final_page = images.len() < page.per_page.try_into().unwrap(); + context.insert("is_final_page", &is_final_page); + + Ok(Html( + View::Images.headers(&headers).render(&ctx.tera, &context)?, + )) } async fn get_image_original( diff --git a/src/server/mod.rs b/src/server/mod.rs index bd7adc9..bb99d8e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -60,9 +60,7 @@ pub async fn render_home(ctx: crate::Context, headers: &HeaderMap) -> Result Result axum::Router { Router::new() @@ -47,9 +47,7 @@ async fn get_plant_page( context.insert("event_types", &db::EVENT_TYPES); Ok(Html( - View::Plant - .headers(&headers) - .render_ctx(&ctx.tera, &context)?, + View::Plant.headers(&headers).render(&ctx.tera, &context)?, )) } None => Err(AppError::NotFound), @@ -58,17 +56,28 @@ async fn get_plant_page( async fn get_plants( State(ctx): State, - Query(page): Query, + Query(mut page): Query, headers: HeaderMap, ) -> super::Result> { let plants = tokio::task::spawn_blocking(move || db::Plant::page(&ctx.pool, page)) .await .unwrap()?; - let list = List::Ul - .items("Plants", ListItem::Plant, plants) - .page("/plants", page); - Ok(Html(View::other(list).headers(&headers).render(&ctx.tera)?)) + let mut context = Context::new(); + context.insert("plants", &plants); + + let is_final_page = plants.len() < page.per_page.try_into().unwrap(); + context.insert("is_final_page", &is_final_page); + + if !is_final_page { + page.page += 1; + + context.insert("query", &page.to_query().encode()); + } + + Ok(Html( + View::Plants.headers(&headers).render(&ctx.tera, &context)?, + )) } async fn post_plant( @@ -81,5 +90,5 @@ async fn post_plant( let mut context = Context::new(); context.insert("plant", &plant); - Ok(Html(Update::PlantLi.render_ctx(&ctx.tera, &context)?)) + Ok(Html(Update::PlantLi.render(&ctx.tera, &context)?)) } diff --git a/src/server/query.rs b/src/server/query.rs index afc49e5..5f66853 100644 --- a/src/server/query.rs +++ b/src/server/query.rs @@ -1,6 +1,6 @@ use serde::Serialize; -#[derive(Serialize, Clone)] +#[derive(Serialize)] pub struct Query(pub Vec<(String, String)>); impl Query { @@ -18,9 +18,3 @@ impl Query { pub trait ToQuery { fn to_query(self) -> Query; } - -impl ToQuery for Query { - fn to_query(self) -> Query { - self - } -} diff --git a/src/template/list.rs b/src/template/list.rs deleted file mode 100644 index 547e1d3..0000000 --- a/src/template/list.rs +++ /dev/null @@ -1,135 +0,0 @@ -use serde::Serialize; -use tera::Context; - -use crate::{ - db::Pagination, - server::query::{Query, ToQuery}, -}; - -use super::Template; - -#[derive(Clone, Copy)] -pub enum List { - Ul, - Card, -} - -pub enum ListItem { - Plant, -} - -pub enum ListCard { - Image, -} - -impl Template for List { - fn template(&self) -> &'static str { - match self { - List::Ul => "list/ul.html", - List::Card => "list/card.html", - } - } -} - -impl Template for ListItem { - fn template(&self) -> &'static str { - match self { - Self::Plant => "li/plant.html", - } - } -} - -impl Template for ListCard { - fn template(&self) -> &'static str { - match self { - Self::Image => "card/image.html", - } - } -} - -impl List { - pub fn items( - self, - heading: &'static str, - item_tmpl: T, - items: Vec, - ) -> ListWrapper { - ListWrapper { - list: self, - heading, - item_tmpl, - items, - next_page: None, - query: None, - } - } -} - -pub struct ListWrapper { - list: List, - heading: &'static str, - item_tmpl: T, - items: Vec, - next_page: Option<(&'static str, Pagination)>, - query: Option, -} - -impl ListWrapper { - pub fn page(mut self, url: &'static str, mut page: Pagination) -> Self { - if self.items.len() as u32 == page.per_page { - page.page += 1; - - self.next_page = Some((url, page)); - } - - self - } - - pub fn query(mut self, query: impl ToQuery) -> Self { - if let Some(old_query) = self.query { - self.query = Some(old_query.join(query)); - } else { - self.query = Some(query.to_query()); - } - - self - } -} - -impl Template for ListWrapper { - fn template(&self) -> &'static str { - self.list.template() - } - - fn render(&self, tera: &tera::Tera) -> tera::Result { - let mut ctx = Context::new(); - - // Render each item separately - let items = self - .items - .iter() - .map(|item| { - ctx.insert("item", &item); - - tera.render(self.item_tmpl.template(), &ctx) - }) - .collect::>>()?; - - ctx.remove("item"); - ctx.insert("heading", &self.heading); - ctx.insert("items", &items); - - if let Some((url, page)) = &self.next_page { - let query = if let Some(query) = &self.query { - query.clone().join(page.clone()) - } else { - page.to_query() - }; - - let full_url = format!("{url}?{}", query.encode()); - ctx.insert("next_page_url", &full_url); - } - - self.render_ctx(tera, &ctx) - } -} diff --git a/src/template/mod.rs b/src/template/mod.rs index 2742ed3..757dbcc 100644 --- a/src/template/mod.rs +++ b/src/template/mod.rs @@ -1,8 +1,6 @@ -mod list; mod update; mod view; -pub use list::{List, ListCard, ListItem}; pub use update::Update; pub use view::View; @@ -11,15 +9,7 @@ pub trait Template { fn template(&self) -> &'static str; /// Render the template with the given context - fn render_ctx(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result { + fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result { tera.render(self.template(), ctx) } - - /// Render the template without providing a context. This is useful for types that manage their - /// own context. - /// - /// By default, this simply renders the template with an empty context. - fn render(&self, tera: &tera::Tera) -> tera::Result { - self.render_ctx(tera, &tera::Context::new()) - } } diff --git a/src/template/view.rs b/src/template/view.rs index f591ad7..6d06da2 100644 --- a/src/template/view.rs +++ b/src/template/view.rs @@ -1,24 +1,20 @@ use axum::http::{HeaderMap, HeaderValue}; -use tera::Context; use super::Template; const HX_REQUEST_HEADER: &str = "HX-Request"; const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request"; +#[derive(Clone, Copy)] pub enum View { Plant, + Plants, Images, Index, Login, - Other(Box), } impl View { - pub fn other(tmpl: impl Template + 'static) -> Self { - View::Other(Box::new(tmpl)) - } - pub fn headers(self, headers: &HeaderMap) -> ViewWrapper { let is_htmx_req = headers.get(HX_REQUEST_HEADER).is_some(); let is_hist_restore_req = headers @@ -39,24 +35,10 @@ impl Template for View { fn template(&self) -> &'static str { match self { View::Plant => "views/plant.html", + View::Plants => "views/plants.html", View::Index => "views/index.html", View::Images => "views/images.html", View::Login => "views/login.html", - View::Other(tmpl) => tmpl.template(), - } - } - - fn render(&self, tera: &tera::Tera) -> tera::Result { - match self { - View::Other(tmpl) => tmpl.render(tera), - _ => tera.render(self.template(), &Context::new()), - } - } - - fn render_ctx(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result { - match self { - View::Other(tmpl) => tmpl.render_ctx(tera, ctx), - _ => tera.render(self.template(), ctx), } } } @@ -66,8 +48,14 @@ pub struct ViewWrapper { include_base: bool, } -impl ViewWrapper { - fn wrap(&self, tera: &tera::Tera, view: String) -> tera::Result { +impl Template for ViewWrapper { + fn template(&self) -> &'static str { + self.view.template() + } + + fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result { + let view = self.view.render(tera, ctx)?; + if self.include_base { let mut ctx = tera::Context::new(); ctx.insert("view", &view); @@ -78,19 +66,3 @@ impl ViewWrapper { } } } - -impl Template for ViewWrapper { - fn template(&self) -> &'static str { - self.view.template() - } - - fn render(&self, tera: &tera::Tera) -> tera::Result { - let view = self.view.render(tera)?; - self.wrap(tera, view) - } - - fn render_ctx(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result { - let view = self.view.render_ctx(tera, ctx)?; - self.wrap(tera, view) - } -} diff --git a/templates/card/image.html b/templates/card/image.html deleted file mode 100644 index 8419b3d..0000000 --- a/templates/card/image.html +++ /dev/null @@ -1,9 +0,0 @@ -{% import "components/plant.html" as comp_plant %} -
-

Date taken: {{ item.0.date_taken }}

-

Note: {{ item.0.note }}

-

Plant: {{ comp_plant::name(plant=item.1) }}

- - - -
diff --git a/templates/li/plant.html b/templates/li/plant.html deleted file mode 100644 index e299da0..0000000 --- a/templates/li/plant.html +++ /dev/null @@ -1,5 +0,0 @@ -{% import "components/plant.html" as comp_plant %} - -
  • - {{ comp_plant::name(plant=item) }} -
  • diff --git a/templates/list/card.html b/templates/list/card.html deleted file mode 100644 index 5bd5021..0000000 --- a/templates/list/card.html +++ /dev/null @@ -1,18 +0,0 @@ -

    {{ heading }}

    - -
    -{% for item in items %} - {{ item | safe }} -{% endfor %} - -{% if next_page_url %} -
    -
    -{% endif %} -
    diff --git a/templates/list/ul.html b/templates/list/ul.html deleted file mode 100644 index 61cb7ab..0000000 --- a/templates/list/ul.html +++ /dev/null @@ -1,18 +0,0 @@ -

    {{ heading }}

    - -
      -{% for item in items %} - {{ item | safe }} -{% endfor %} - -{% if next_page_url %} -
      -
      -{% endif %} -