feat: add comments
							parent
							
								
									fed9c01370
								
							
						
					
					
						commit
						cc69935a88
					
				|  | @ -1,6 +1,8 @@ | |||
| use r2d2_sqlite::rusqlite::{self, Row}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use super::{DbError, DbPool}; | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| pub struct Comment { | ||||
|     id: i32, | ||||
|  | @ -8,6 +10,12 @@ pub struct Comment { | |||
|     comment: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Deserialize)] | ||||
| pub struct NewComment { | ||||
|     plant_id: i32, | ||||
|     comment: String, | ||||
| } | ||||
| 
 | ||||
| impl Comment { | ||||
|     pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> { | ||||
|         Ok(Self { | ||||
|  | @ -17,3 +25,13 @@ impl Comment { | |||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl NewComment { | ||||
|     pub fn insert(self, pool: &DbPool) -> Result<Comment, DbError> { | ||||
|         let conn = pool.get()?; | ||||
| 
 | ||||
|         let mut stmt = | ||||
|             conn.prepare("insert into comments (plant_id, comment) values ($1, $2) returning *")?; | ||||
|         Ok(stmt.query_row((self.plant_id, self.comment), Comment::from_row)?) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ use r2d2_sqlite::{rusqlite, SqliteConnectionManager}; | |||
| 
 | ||||
| use std::{error::Error, fmt}; | ||||
| 
 | ||||
| pub use comment::Comment; | ||||
| pub use comment::{Comment, NewComment}; | ||||
| pub use plant::{NewPlant, Plant}; | ||||
| 
 | ||||
| pub type DbPool = r2d2::Pool<SqliteConnectionManager>; | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ impl Plant { | |||
| 
 | ||||
|     pub fn comments(&self, pool: &DbPool) -> Result<Vec<Comment>, DbError> { | ||||
|         let conn = pool.get()?; | ||||
|         let mut stmt = conn.prepare("select * from plant_comments where plant_id = $1")?; | ||||
|         let mut stmt = conn.prepare("select * from comments where plant_id = $1")?; | ||||
| 
 | ||||
|         let comments: Result<Vec<_>, _> = stmt.query_map((self.id,), Comment::from_row)?.collect(); | ||||
|         Ok(comments?) | ||||
|  |  | |||
|  | @ -99,6 +99,10 @@ fn load_templates() -> Tera { | |||
|             "partials/plant_info.html", | ||||
|             include_str!("templates/partials/plant_info.html"), | ||||
|         ), | ||||
|         ( | ||||
|             "partials/comment_li.html", | ||||
|             include_str!("templates/partials/comment_li.html"), | ||||
|         ), | ||||
|     ]) | ||||
|     .unwrap(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,26 @@ | |||
| use axum::{extract::State, response::Html, routing::post, Form, Router}; | ||||
| use tera::Context; | ||||
| 
 | ||||
| use crate::db::{Comment, NewComment}; | ||||
| 
 | ||||
| pub fn app(ctx: crate::Context) -> axum::Router { | ||||
|     Router::new().route("/", post(post_comment)).with_state(ctx) | ||||
| } | ||||
| 
 | ||||
| async fn post_comment( | ||||
|     State(ctx): State<crate::Context>, | ||||
|     Form(comment): Form<NewComment>, | ||||
| ) -> Html<String> { | ||||
|     let comment = tokio::task::spawn_blocking(move || comment.insert(&ctx.pool)) | ||||
|         .await | ||||
|         .unwrap() | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     let mut context = Context::new(); | ||||
|     context.insert("comment", &comment); | ||||
|     Html( | ||||
|         ctx.tera | ||||
|             .render("partials/comment_li.html", &context) | ||||
|             .unwrap(), | ||||
|     ) | ||||
| } | ||||
|  | @ -1,3 +1,4 @@ | |||
| mod comments; | ||||
| mod plants; | ||||
| 
 | ||||
| use axum::{ | ||||
|  | @ -29,7 +30,8 @@ pub fn app(ctx: crate::Context) -> axum::Router { | |||
|     let mut router = Router::new() | ||||
|         .route("/", get(get_index)) | ||||
|         .with_state(ctx.clone()) | ||||
|         .nest("/plants", plants::app(ctx.clone())); | ||||
|         .nest("/plants", plants::app(ctx.clone())) | ||||
|         .nest("/comments", comments::app(ctx.clone())); | ||||
| 
 | ||||
|     for (name, content) in crate::STATIC_FILES { | ||||
|         router = router.route(&format!("/static/{}", name), get(content)) | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| <li>{{ comment.comment }}</li> | ||||
|  | @ -12,7 +12,8 @@ | |||
|     </li> | ||||
| {% endfor %} | ||||
| </ul> | ||||
| <form hx-post="/plants/{{ plant.id }}/comments" hx-target="#comments" hx-swap="beforeend"> | ||||
| <form hx-post="/comments" hx-target="#comments" hx-swap="beforeend"> | ||||
|     <input type="hidden" id="plant_id" name="plant_id" value="{{ plant.id }}"> | ||||
|     <label for="comment">Comment:</label> | ||||
|     <textarea id="comment" name="comment" rows=4></textarea></br> | ||||
|     <input type="submit"> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue