simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

comment.rs (3328B)


      1 use askama::Template;
      2 use axum::{
      3     extract::{Path, State},
      4     response::{Html, Redirect},
      5     Form,
      6 };
      7 use axum_extra::extract::cookie::CookieJar;
      8 use serde::Deserialize;
      9 
     10 use crate::error::AppError;
     11 use crate::state::AppState;
     12 use crate::templates::{ApplicationTemplate, ReplyFormTemplate};
     13 
     14 #[derive(Debug, Deserialize)]
     15 pub struct CommentForm {
     16     pub text: String,
     17 }
     18 
     19 /// Create a top-level comment on a story
     20 pub async fn create_comment(
     21     State(state): State<AppState>,
     22     jar: CookieJar,
     23     Path(story_id): Path<i64>,
     24     Form(form): Form<CommentForm>,
     25 ) -> Result<Redirect, Html<String>> {
     26     let user_id = state.get_user_id(&jar).await.ok_or_else(|| {
     27         Html(r#"<meta http-equiv="refresh" content="0;url=/login">"#.to_string())
     28     })?;
     29 
     30     if form.text.trim().is_empty() {
     31         return Err(Html("Comment text is required".to_string()));
     32     }
     33 
     34     let text = form.text.trim().to_string();
     35     let comment_tx = state.comment_tx.clone();
     36 
     37     state.db().create_comment(user_id, story_id, None, &text)
     38         .await.map_err(|e| Html(format!("Failed to create comment: {}", e)))?;
     39 
     40     // Notify SSE subscribers
     41     let _ = comment_tx.send(story_id);
     42 
     43     Ok(Redirect::to(&format!("/story/{}", story_id)))
     44 }
     45 
     46 /// Show reply form for a comment
     47 pub async fn show_reply_form(
     48     State(state): State<AppState>,
     49     jar: CookieJar,
     50     Path(comment_id): Path<i64>,
     51 ) -> Result<Html<String>, AppError> {
     52     let user_id = state.get_user_id(&jar).await;
     53 
     54     if user_id.is_none() {
     55         return Ok(Html(
     56             r#"<meta http-equiv="refresh" content="0;url=/login">"#.to_string(),
     57         ));
     58     }
     59 
     60     let db = state.db();
     61     let parent_comment = db.get_comment(comment_id).await?
     62         .ok_or_else(|| AppError::NotFound("Comment not found".to_string()))?;
     63 
     64     let parent_author = if let Ok(uid) = parent_comment.created_by.parse::<i64>() {
     65         db.get_user_by_id(uid)
     66             .await
     67             .ok()
     68             .flatten()
     69             .map(|u| u.username)
     70     } else {
     71         None
     72     };
     73 
     74     let reply_form = ReplyFormTemplate {
     75         comment_id,
     76         story_id: parent_comment.story_id,
     77         parent_text: parent_comment.text,
     78         parent_author,
     79         error: None,
     80     };
     81 
     82     let app = ApplicationTemplate {
     83         content: reply_form.render()?,
     84     };
     85     Ok(Html(app.render()?))
     86 }
     87 
     88 #[derive(Debug, Deserialize)]
     89 pub struct ReplyForm {
     90     pub text: String,
     91     pub story_id: i64,
     92 }
     93 
     94 /// Submit a reply to a comment
     95 pub async fn submit_reply(
     96     State(state): State<AppState>,
     97     jar: CookieJar,
     98     Path(parent_id): Path<i64>,
     99     Form(form): Form<ReplyForm>,
    100 ) -> Result<Redirect, Html<String>> {
    101     let user_id = state.get_user_id(&jar).await.ok_or_else(|| {
    102         Html(r#"<meta http-equiv="refresh" content="0;url=/login">"#.to_string())
    103     })?;
    104 
    105     if form.text.trim().is_empty() {
    106         return Err(Html("Reply text is required".to_string()));
    107     }
    108 
    109     let text = form.text.trim().to_string();
    110     let story_id = form.story_id;
    111     let comment_tx = state.comment_tx.clone();
    112 
    113     state.db().create_comment(user_id, story_id, Some(parent_id), &text)
    114         .await.map_err(|e| Html(format!("Failed to create reply: {}", e)))?;
    115 
    116     // Notify SSE subscribers
    117     let _ = comment_tx.send(story_id);
    118 
    119     Ok(Redirect::to(&format!("/story/{}", story_id)))
    120 }