The smokesignal.events web application
1use axum::Json;
2use axum::extract::State;
3use axum::http::StatusCode;
4use axum::response::IntoResponse;
5use axum_extra::extract::Cached;
6use serde::{Deserialize, Serialize};
7use serde_json::json;
8
9use crate::facets::{FacetLimits, parse_facets_from_text, render_text_with_facets_html};
10use crate::http::context::WebContext;
11use crate::http::errors::WebError;
12use crate::http::middleware_auth::Auth;
13
14#[derive(Debug, Deserialize)]
15pub(crate) struct PreviewDescriptionRequest {
16 pub description: String,
17}
18
19#[derive(Debug, Serialize)]
20pub(crate) struct PreviewDescriptionResponse {
21 pub html: String,
22}
23
24/// Handler for previewing event descriptions with rendered facets
25///
26/// This endpoint accepts description text, parses facets (mentions, links, hashtags),
27/// and returns rendered HTML showing how the description will appear to users.
28pub(crate) async fn handle_preview_description(
29 State(web_context): State<WebContext>,
30 Cached(_auth): Cached<Auth>,
31 Json(request): Json<PreviewDescriptionRequest>,
32) -> Result<impl IntoResponse, WebError> {
33 // Validate description length (same as create event validation)
34 if request.description.trim().len() < 10 {
35 return Ok((
36 StatusCode::BAD_REQUEST,
37 Json(json!({"error": "Description must be at least 10 characters"})),
38 )
39 .into_response());
40 }
41
42 if request.description.len() > 3000 {
43 return Ok((
44 StatusCode::BAD_REQUEST,
45 Json(json!({"error": "Description must be no more than 3000 characters"})),
46 )
47 .into_response());
48 }
49
50 // Trim description to match create/edit behavior - facets must be parsed from the same text
51 let description = request.description.trim();
52
53 // Parse facets from the description
54 let limits = FacetLimits::default();
55 let facets =
56 parse_facets_from_text(description, web_context.identity_resolver.as_ref(), &limits).await;
57
58 // Render HTML with facets
59 let html = render_text_with_facets_html(description, facets.as_ref(), &limits);
60
61 // Convert newlines to <br> tags for proper display
62 let html_with_breaks = html.replace('\n', "<br>");
63
64 Ok((
65 StatusCode::OK,
66 Json(PreviewDescriptionResponse {
67 html: html_with_breaks,
68 }),
69 )
70 .into_response())
71}