use axum::Json; use axum::extract::State; use axum::http::StatusCode; use axum::response::IntoResponse; use axum_extra::extract::Cached; use serde::{Deserialize, Serialize}; use serde_json::json; use crate::facets::{FacetLimits, parse_facets_from_text, render_text_with_facets_html}; use crate::http::context::WebContext; use crate::http::errors::WebError; use crate::http::middleware_auth::Auth; #[derive(Debug, Deserialize)] pub(crate) struct PreviewDescriptionRequest { pub description: String, } #[derive(Debug, Serialize)] pub(crate) struct PreviewDescriptionResponse { pub html: String, } /// Handler for previewing event descriptions with rendered facets /// /// This endpoint accepts description text, parses facets (mentions, links, hashtags), /// and returns rendered HTML showing how the description will appear to users. pub(crate) async fn handle_preview_description( State(web_context): State, Cached(_auth): Cached, Json(request): Json, ) -> Result { // Validate description length (same as create event validation) if request.description.trim().len() < 10 { return Ok(( StatusCode::BAD_REQUEST, Json(json!({"error": "Description must be at least 10 characters"})), ) .into_response()); } if request.description.len() > 3000 { return Ok(( StatusCode::BAD_REQUEST, Json(json!({"error": "Description must be no more than 3000 characters"})), ) .into_response()); } // Trim description to match create/edit behavior - facets must be parsed from the same text let description = request.description.trim(); // Parse facets from the description let limits = FacetLimits::default(); let facets = parse_facets_from_text(description, web_context.identity_resolver.as_ref(), &limits).await; // Render HTML with facets let html = render_text_with_facets_html(description, facets.as_ref(), &limits); // Convert newlines to
tags for proper display let html_with_breaks = html.replace('\n', "
"); Ok(( StatusCode::OK, Json(PreviewDescriptionResponse { html: html_with_breaks, }), ) .into_response()) }