Compare commits
	
		
			2 Commits 
		
	
	
		
			1b22c8d118
			...
			b17c2e7df6
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						b17c2e7df6 | |
| 
							
							
								
									
								
								 | 
						4e92c02e63 | 
| 
						 | 
					@ -20,5 +20,5 @@ async fn post_event(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut context = Context::new();
 | 
					    let mut context = Context::new();
 | 
				
			||||||
    context.insert("event", &event);
 | 
					    context.insert("event", &event);
 | 
				
			||||||
    Ok(Html(Update::EventLi.render(&ctx.tera, &context)?))
 | 
					    Ok(Html(Update::EventLi.render_ctx(&ctx.tera, &context)?))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ use std::{io::BufWriter, path::PathBuf};
 | 
				
			||||||
use super::error::AppError;
 | 
					use super::error::AppError;
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    db::{self, Image, NewImage, Pagination},
 | 
					    db::{self, Image, NewImage, Pagination},
 | 
				
			||||||
    template::{Template, View},
 | 
					    template::{List, ListCard, Template, View},
 | 
				
			||||||
    IMG_DIR,
 | 
					    IMG_DIR,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,15 +72,11 @@ async fn get_images(
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .unwrap()?;
 | 
					            .unwrap()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut context = Context::new();
 | 
					    let list = List::Card
 | 
				
			||||||
    context.insert("images", &images);
 | 
					        .items("Images", ListCard::Image, images)
 | 
				
			||||||
 | 
					        .page("/images", page)
 | 
				
			||||||
    let is_final_page = images.len() < page.per_page.try_into().unwrap();
 | 
					        .query(filter);
 | 
				
			||||||
    context.insert("is_final_page", &is_final_page);
 | 
					    Ok(Html(View::other(list).headers(&headers).render(&ctx.tera)?))
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(Html(
 | 
					 | 
				
			||||||
        View::Images.headers(&headers).render(&ctx.tera, &context)?,
 | 
					 | 
				
			||||||
    ))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn get_image_original(
 | 
					async fn get_image_original(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,9 @@ pub async fn render_home(ctx: crate::Context, headers: &HeaderMap) -> Result<Htm
 | 
				
			||||||
    context.insert("plants", &plants);
 | 
					    context.insert("plants", &plants);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(Html(
 | 
					    Ok(Html(
 | 
				
			||||||
        View::Index.headers(headers).render(&ctx.tera, &context)?,
 | 
					        View::Index
 | 
				
			||||||
 | 
					            .headers(headers)
 | 
				
			||||||
 | 
					            .render_ctx(&ctx.tera, &context)?,
 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,7 +70,9 @@ pub fn render_login(ctx: crate::Context, headers: &HeaderMap) -> Result<Html<Str
 | 
				
			||||||
    let context = Context::new();
 | 
					    let context = Context::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(Html(
 | 
					    Ok(Html(
 | 
				
			||||||
        View::Login.headers(headers).render(&ctx.tera, &context)?,
 | 
					        View::Login
 | 
				
			||||||
 | 
					            .headers(headers)
 | 
				
			||||||
 | 
					            .render_ctx(&ctx.tera, &context)?,
 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,10 @@ use tera::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    db::{self, DbError, Event, Pagination, Plant},
 | 
					    db::{self, DbError, Event, Pagination, Plant},
 | 
				
			||||||
    template::{Template, Update, View},
 | 
					    template::{List, ListItem, Template, Update, View},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{error::AppError, query::ToQuery};
 | 
					use super::error::AppError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn app() -> axum::Router<crate::Context> {
 | 
					pub fn app() -> axum::Router<crate::Context> {
 | 
				
			||||||
    Router::new()
 | 
					    Router::new()
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,9 @@ async fn get_plant_page(
 | 
				
			||||||
            context.insert("event_types", &db::EVENT_TYPES);
 | 
					            context.insert("event_types", &db::EVENT_TYPES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok(Html(
 | 
					            Ok(Html(
 | 
				
			||||||
                View::Plant.headers(&headers).render(&ctx.tera, &context)?,
 | 
					                View::Plant
 | 
				
			||||||
 | 
					                    .headers(&headers)
 | 
				
			||||||
 | 
					                    .render_ctx(&ctx.tera, &context)?,
 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        None => Err(AppError::NotFound),
 | 
					        None => Err(AppError::NotFound),
 | 
				
			||||||
| 
						 | 
					@ -56,28 +58,17 @@ async fn get_plant_page(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn get_plants(
 | 
					async fn get_plants(
 | 
				
			||||||
    State(ctx): State<crate::Context>,
 | 
					    State(ctx): State<crate::Context>,
 | 
				
			||||||
    Query(mut page): Query<Pagination>,
 | 
					    Query(page): Query<Pagination>,
 | 
				
			||||||
    headers: HeaderMap,
 | 
					    headers: HeaderMap,
 | 
				
			||||||
) -> super::Result<Html<String>> {
 | 
					) -> super::Result<Html<String>> {
 | 
				
			||||||
    let plants = tokio::task::spawn_blocking(move || db::Plant::page(&ctx.pool, page))
 | 
					    let plants = tokio::task::spawn_blocking(move || db::Plant::page(&ctx.pool, page))
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .unwrap()?;
 | 
					        .unwrap()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut context = Context::new();
 | 
					    let list = List::Ul
 | 
				
			||||||
    context.insert("plants", &plants);
 | 
					        .items("Plants", ListItem::Plant, plants)
 | 
				
			||||||
 | 
					        .page("/plants", page);
 | 
				
			||||||
    let is_final_page = plants.len() < page.per_page.try_into().unwrap();
 | 
					    Ok(Html(View::other(list).headers(&headers).render(&ctx.tera)?))
 | 
				
			||||||
    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(
 | 
					async fn post_plant(
 | 
				
			||||||
| 
						 | 
					@ -90,5 +81,5 @@ async fn post_plant(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut context = Context::new();
 | 
					    let mut context = Context::new();
 | 
				
			||||||
    context.insert("plant", &plant);
 | 
					    context.insert("plant", &plant);
 | 
				
			||||||
    Ok(Html(Update::PlantLi.render(&ctx.tera, &context)?))
 | 
					    Ok(Html(Update::PlantLi.render_ctx(&ctx.tera, &context)?))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
use serde::Serialize;
 | 
					use serde::Serialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize)]
 | 
					#[derive(Serialize, Clone)]
 | 
				
			||||||
pub struct Query(pub Vec<(String, String)>);
 | 
					pub struct Query(pub Vec<(String, String)>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Query {
 | 
					impl Query {
 | 
				
			||||||
| 
						 | 
					@ -18,3 +18,9 @@ impl Query {
 | 
				
			||||||
pub trait ToQuery {
 | 
					pub trait ToQuery {
 | 
				
			||||||
    fn to_query(self) -> Query;
 | 
					    fn to_query(self) -> Query;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ToQuery for Query {
 | 
				
			||||||
 | 
					    fn to_query(self) -> Query {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					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<T: Template, I: Serialize>(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        heading: &'static str,
 | 
				
			||||||
 | 
					        item_tmpl: T,
 | 
				
			||||||
 | 
					        items: Vec<I>,
 | 
				
			||||||
 | 
					    ) -> ListWrapper<T, I> {
 | 
				
			||||||
 | 
					        ListWrapper {
 | 
				
			||||||
 | 
					            list: self,
 | 
				
			||||||
 | 
					            heading,
 | 
				
			||||||
 | 
					            item_tmpl,
 | 
				
			||||||
 | 
					            items,
 | 
				
			||||||
 | 
					            next_page: None,
 | 
				
			||||||
 | 
					            query: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct ListWrapper<T: Template, I: Serialize> {
 | 
				
			||||||
 | 
					    list: List,
 | 
				
			||||||
 | 
					    heading: &'static str,
 | 
				
			||||||
 | 
					    item_tmpl: T,
 | 
				
			||||||
 | 
					    items: Vec<I>,
 | 
				
			||||||
 | 
					    next_page: Option<(&'static str, Pagination)>,
 | 
				
			||||||
 | 
					    query: Option<Query>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: Template, I: Serialize> ListWrapper<T, I> {
 | 
				
			||||||
 | 
					    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<T: Template, I: Serialize> Template for ListWrapper<T, I> {
 | 
				
			||||||
 | 
					    fn template(&self) -> &'static str {
 | 
				
			||||||
 | 
					        self.list.template()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render(&self, tera: &tera::Tera) -> tera::Result<String> {
 | 
				
			||||||
 | 
					        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::<tera::Result<Vec<String>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
 | 
					mod list;
 | 
				
			||||||
mod update;
 | 
					mod update;
 | 
				
			||||||
mod view;
 | 
					mod view;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use list::{List, ListCard, ListItem};
 | 
				
			||||||
pub use update::Update;
 | 
					pub use update::Update;
 | 
				
			||||||
pub use view::View;
 | 
					pub use view::View;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +11,15 @@ pub trait Template {
 | 
				
			||||||
    fn template(&self) -> &'static str;
 | 
					    fn template(&self) -> &'static str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Render the template with the given context
 | 
					    /// Render the template with the given context
 | 
				
			||||||
    fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
					    fn render_ctx(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
				
			||||||
        tera.render(self.template(), ctx)
 | 
					        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<String> {
 | 
				
			||||||
 | 
					        self.render_ctx(tera, &tera::Context::new())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,24 @@
 | 
				
			||||||
use axum::http::{HeaderMap, HeaderValue};
 | 
					use axum::http::{HeaderMap, HeaderValue};
 | 
				
			||||||
 | 
					use tera::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::Template;
 | 
					use super::Template;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const HX_REQUEST_HEADER: &str = "HX-Request";
 | 
					const HX_REQUEST_HEADER: &str = "HX-Request";
 | 
				
			||||||
const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request";
 | 
					const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
pub enum View {
 | 
					pub enum View {
 | 
				
			||||||
    Plant,
 | 
					    Plant,
 | 
				
			||||||
    Plants,
 | 
					 | 
				
			||||||
    Images,
 | 
					    Images,
 | 
				
			||||||
    Index,
 | 
					    Index,
 | 
				
			||||||
    Login,
 | 
					    Login,
 | 
				
			||||||
 | 
					    Other(Box<dyn Template>),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl View {
 | 
					impl View {
 | 
				
			||||||
 | 
					    pub fn other(tmpl: impl Template + 'static) -> Self {
 | 
				
			||||||
 | 
					        View::Other(Box::new(tmpl))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn headers(self, headers: &HeaderMap) -> ViewWrapper {
 | 
					    pub fn headers(self, headers: &HeaderMap) -> ViewWrapper {
 | 
				
			||||||
        let is_htmx_req = headers.get(HX_REQUEST_HEADER).is_some();
 | 
					        let is_htmx_req = headers.get(HX_REQUEST_HEADER).is_some();
 | 
				
			||||||
        let is_hist_restore_req = headers
 | 
					        let is_hist_restore_req = headers
 | 
				
			||||||
| 
						 | 
					@ -35,10 +39,24 @@ impl Template for View {
 | 
				
			||||||
    fn template(&self) -> &'static str {
 | 
					    fn template(&self) -> &'static str {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            View::Plant => "views/plant.html",
 | 
					            View::Plant => "views/plant.html",
 | 
				
			||||||
            View::Plants => "views/plants.html",
 | 
					 | 
				
			||||||
            View::Index => "views/index.html",
 | 
					            View::Index => "views/index.html",
 | 
				
			||||||
            View::Images => "views/images.html",
 | 
					            View::Images => "views/images.html",
 | 
				
			||||||
            View::Login => "views/login.html",
 | 
					            View::Login => "views/login.html",
 | 
				
			||||||
 | 
					            View::Other(tmpl) => tmpl.template(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render(&self, tera: &tera::Tera) -> tera::Result<String> {
 | 
				
			||||||
 | 
					        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<String> {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            View::Other(tmpl) => tmpl.render_ctx(tera, ctx),
 | 
				
			||||||
 | 
					            _ => tera.render(self.template(), ctx),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -48,14 +66,8 @@ pub struct ViewWrapper {
 | 
				
			||||||
    include_base: bool,
 | 
					    include_base: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Template for ViewWrapper {
 | 
					impl ViewWrapper {
 | 
				
			||||||
    fn template(&self) -> &'static str {
 | 
					    fn wrap(&self, tera: &tera::Tera, view: String) -> tera::Result<String> {
 | 
				
			||||||
        self.view.template()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
					 | 
				
			||||||
        let view = self.view.render(tera, ctx)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.include_base {
 | 
					        if self.include_base {
 | 
				
			||||||
            let mut ctx = tera::Context::new();
 | 
					            let mut ctx = tera::Context::new();
 | 
				
			||||||
            ctx.insert("view", &view);
 | 
					            ctx.insert("view", &view);
 | 
				
			||||||
| 
						 | 
					@ -66,3 +78,19 @@ impl Template for ViewWrapper {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Template for ViewWrapper {
 | 
				
			||||||
 | 
					    fn template(&self) -> &'static str {
 | 
				
			||||||
 | 
					        self.view.template()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render(&self, tera: &tera::Tera) -> tera::Result<String> {
 | 
				
			||||||
 | 
					        let view = self.view.render(tera)?;
 | 
				
			||||||
 | 
					        self.wrap(tera, view)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render_ctx(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
				
			||||||
 | 
					        let view = self.view.render_ctx(tera, ctx)?;
 | 
				
			||||||
 | 
					        self.wrap(tera, view)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					{% import "components/plant.html" as comp_plant %}
 | 
				
			||||||
 | 
					<article>
 | 
				
			||||||
 | 
					    <p>Date taken: {{ item.0.date_taken }}</p>
 | 
				
			||||||
 | 
					    <p>Note: {{ item.0.note }}</p>
 | 
				
			||||||
 | 
					    <p>Plant: {{ comp_plant::name(plant=item.1) }}</p>
 | 
				
			||||||
 | 
					    <a href="/images/{{ item.0.id }}/original" target="_blank">
 | 
				
			||||||
 | 
					        <img src="/images/{{ item.0.id }}/thumb">
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</article>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					{% import "components/plant.html" as comp_plant %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<li>
 | 
				
			||||||
 | 
					    {{ comp_plant::name(plant=item) }} 
 | 
				
			||||||
 | 
					</li>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					<h1>{{ heading }}</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div id="items">
 | 
				
			||||||
 | 
					{% for item in items %}
 | 
				
			||||||
 | 
					    {{ item | safe }}
 | 
				
			||||||
 | 
					{% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if next_page_url %}
 | 
				
			||||||
 | 
					<div 
 | 
				
			||||||
 | 
					    aria-busy="true"
 | 
				
			||||||
 | 
					    hx-get="{{ next_page_url }}"
 | 
				
			||||||
 | 
					    hx-select="#items > *"
 | 
				
			||||||
 | 
					    hx-trigger="revealed"
 | 
				
			||||||
 | 
					    hx-target="this"
 | 
				
			||||||
 | 
					    hx-swap="outerHTML">
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					<h1>{{ heading }}</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ul id="items">
 | 
				
			||||||
 | 
					{% for item in items %}
 | 
				
			||||||
 | 
					    {{ item | safe }}
 | 
				
			||||||
 | 
					{% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if next_page_url %}
 | 
				
			||||||
 | 
					<div 
 | 
				
			||||||
 | 
					    aria-busy="true"
 | 
				
			||||||
 | 
					    hx-get="{{ next_page_url }}"
 | 
				
			||||||
 | 
					    hx-select="#items > *"
 | 
				
			||||||
 | 
					    hx-trigger="revealed"
 | 
				
			||||||
 | 
					    hx-target="this"
 | 
				
			||||||
 | 
					    hx-swap="outerHTML">
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
		Loading…
	
		Reference in New Issue