i18n+filtering fork - fluent-templates v2

Handle migration to use new templating renderer with i18n

+26 -34
src/http/handle_admin_denylist.rs
··· 4 4 response::{IntoResponse, Redirect}, 5 5 Form, 6 6 }; 7 - use axum_template::RenderHtml; 8 7 use minijinja::context as template_context; 9 8 use serde::Deserialize; 10 9 use std::borrow::Cow; 11 10 12 11 use crate::{ 13 - contextual_error, 12 + contextual_error, create_renderer, 14 13 http::{ 15 - context::{admin_template_context, AdminRequestContext}, 14 + context::AdminRequestContext, 16 15 errors::WebError, 17 16 pagination::{Pagination, PaginationView}, 18 17 }, 19 - select_template, 20 18 storage::denylist::{denylist_add_or_update, denylist_list, denylist_remove}, 21 19 }; 22 20 ··· 39 37 "https://{}/admin/denylist", 40 38 admin_ctx.web_context.config.external_base 41 39 ); 42 - let default_context = admin_template_context(&admin_ctx, &canonical_url); 43 40 44 - let render_template = select_template!("admin_denylist", false, false, admin_ctx.language); 45 - let error_template = select_template!(false, false, admin_ctx.language); 41 + let renderer = create_renderer!(admin_ctx.web_context, admin_ctx.language, false, false); 46 42 47 43 let (page, page_size) = pagination.admin_clamped(); 48 44 49 45 let denylist = denylist_list(&admin_ctx.web_context.pool, page, page_size).await; 50 46 if let Err(err) = denylist { 51 47 return contextual_error!( 52 - admin_ctx.web_context, 53 - admin_ctx.language, 54 - error_template, 55 - default_context, 56 - err 48 + renderer: renderer, 49 + err, 50 + template_context!{} 57 51 ); 58 52 } 59 53 let (total_count, mut entries) = denylist.unwrap(); ··· 66 60 entries.truncate(page_size as usize); 67 61 } 68 62 69 - Ok(RenderHtml( 70 - &render_template, 71 - admin_ctx.web_context.engine.clone(), 72 - template_context! { ..default_context, ..template_context! { 73 - entries, 74 - total_count, 75 - pagination => pagination_view, 76 - }}, 77 - ) 78 - .into_response()) 63 + Ok(renderer 64 + .render_template( 65 + "admin_denylist", 66 + template_context! { 67 + entries, 68 + total_count, 69 + pagination => pagination_view, 70 + }, 71 + Some(&admin_ctx.current_handle), 72 + &canonical_url, 73 + ) 74 + .into_response()) 79 75 } 80 76 81 77 pub async fn handle_admin_denylist_add( 82 78 admin_ctx: AdminRequestContext, 83 79 Form(form): Form<DenylistAddForm>, 84 80 ) -> Result<impl IntoResponse, WebError> { 85 - let error_template = select_template!(false, false, admin_ctx.language); 81 + let renderer = create_renderer!(admin_ctx.web_context, admin_ctx.language, false, false); 86 82 87 83 if let Err(err) = denylist_add_or_update( 88 84 &admin_ctx.web_context.pool, ··· 92 88 .await 93 89 { 94 90 return contextual_error!( 95 - admin_ctx.web_context, 96 - admin_ctx.language, 97 - error_template, 98 - template_context! {}, 99 - err 91 + renderer: renderer, 92 + err, 93 + template_context!{} 100 94 ); 101 95 } 102 96 ··· 107 101 admin_ctx: AdminRequestContext, 108 102 Form(form): Form<DenylistRemoveForm>, 109 103 ) -> Result<impl IntoResponse, WebError> { 110 - let error_template = select_template!(false, false, admin_ctx.language); 104 + let renderer = create_renderer!(admin_ctx.web_context, admin_ctx.language, false, false); 111 105 112 106 if let Err(err) = denylist_remove(&admin_ctx.web_context.pool, &form.subject).await { 113 107 return contextual_error!( 114 - admin_ctx.web_context, 115 - admin_ctx.language, 116 - error_template, 117 - template_context! {}, 118 - err 108 + renderer: renderer, 109 + err, 110 + template_context!{} 119 111 ); 120 112 } 121 113
+9 -28
src/http/handle_admin_event.rs
··· 1 1 use anyhow::Result; 2 2 use axum::{extract::Query, response::IntoResponse}; 3 - use axum_template::RenderHtml; 4 3 use minijinja::context as template_context; 5 4 use serde::Deserialize; 6 5 7 6 use crate::{ 8 - contextual_error, 7 + contextual_error, create_renderer, 9 8 http::{context::AdminRequestContext, errors::WebError}, 10 - select_template, 11 9 storage::event::event_get, 12 10 }; 13 11 ··· 25 23 26 24 let canonical_url = format!("https://{}/admin/event", web_context.config.external_base); 27 25 28 - // Create context with query parameters 29 - let context_with_aturi = template_context! { 30 - language => language.to_string(), 31 - current_handle => admin_ctx.admin_handle.clone(), 32 - canonical_url => canonical_url, 33 - aturi => query.aturi.clone() 34 - }; 35 - 36 - let render_template = select_template!("admin_event", false, false, language); 37 - let error_template = select_template!(false, false, language); 26 + // Create the template renderer with enhanced context 27 + let renderer = create_renderer!(web_context.clone(), language, false, false); 38 28 39 29 // Get the event record by AT-URI 40 30 let event = event_get(&web_context.pool, &query.aturi).await; 41 31 if let Err(err) = event { 42 - return contextual_error!( 43 - web_context, 44 - language.0, 45 - error_template, 46 - context_with_aturi, 47 - err 48 - ); 32 + return contextual_error!(renderer: renderer, err, template_context!{ aturi => query.aturi.clone() }); 49 33 } 50 34 let event = event.unwrap(); 51 35 ··· 53 37 let event_json = serde_json::to_string_pretty(&event) 54 38 .unwrap_or_else(|_| "Error formatting JSON".to_string()); 55 39 56 - Ok(RenderHtml( 57 - &render_template, 58 - web_context.engine.clone(), 40 + Ok(renderer.render_template( 41 + "admin_event", 59 42 template_context! { 60 - language => language.to_string(), 61 - current_handle => admin_ctx.admin_handle.clone(), 62 - canonical_url => canonical_url, 63 43 aturi => query.aturi.clone(), 64 44 event => event, 65 45 event_json => event_json, 66 46 }, 67 - ) 68 - .into_response()) 47 + Some(&admin_ctx.admin_handle), 48 + &canonical_url, 49 + )?) 69 50 }
+9 -25
src/http/handle_admin_events.rs
··· 1 1 use anyhow::Result; 2 2 use axum::{extract::Query, response::IntoResponse}; 3 - use axum_template::RenderHtml; 4 3 use minijinja::context as template_context; 5 4 6 5 use crate::{ 7 - contextual_error, 6 + contextual_error, create_renderer, 8 7 http::{ 9 8 context::AdminRequestContext, 10 9 errors::WebError, 11 10 pagination::{Pagination, PaginationView}, 12 11 }, 13 - select_template, 14 12 storage::event::event_list, 15 13 }; 16 14 ··· 22 20 let web_context = admin_ctx.web_context; 23 21 24 22 let canonical_url = format!("https://{}/admin/events", web_context.config.external_base); 25 - let default_context = template_context! { 26 - language => language.to_string(), 27 - current_handle => admin_ctx.admin_handle.clone(), 28 - canonical_url => canonical_url, 29 - }; 30 23 31 - let render_template = select_template!("admin_events", false, false, language); 32 - let error_template = select_template!(false, false, language); 24 + // Create the template renderer with enhanced context 25 + let renderer = create_renderer!(web_context.clone(), language, false, false); 33 26 34 27 let (page, page_size) = pagination.admin_clamped(); 35 28 36 29 let events = event_list(&web_context.pool, page, page_size).await; 37 30 if let Err(err) = events { 38 - return contextual_error!( 39 - web_context, 40 - language.0, 41 - error_template, 42 - default_context, 43 - err 44 - ); 31 + return contextual_error!(renderer: renderer, err, template_context!{}); 45 32 } 46 33 let (total_count, mut events) = events.unwrap(); 47 34 ··· 53 40 events.truncate(page_size as usize); 54 41 } 55 42 56 - Ok(RenderHtml( 57 - &render_template, 58 - web_context.engine.clone(), 43 + Ok(renderer.render_template( 44 + "admin_events", 59 45 template_context! { 60 - language => language.to_string(), 61 - current_handle => admin_ctx.admin_handle.clone(), 62 - canonical_url => canonical_url, 63 46 events => events, 64 47 total_count => total_count, 65 48 pagination => pagination_view, 66 49 }, 67 - ) 68 - .into_response()) 50 + Some(&admin_ctx.admin_handle.handle), 51 + &canonical_url, 52 + )) 69 53 }
+12 -21
src/http/handle_admin_handles.rs
··· 4 4 response::{IntoResponse, Redirect}, 5 5 }; 6 6 use axum_htmx::{HxRedirect, HxRequest}; 7 - use axum_template::RenderHtml; 8 7 use http::StatusCode; 9 8 use minijinja::context as template_context; 10 9 11 10 use crate::{ 12 - contextual_error, 11 + contextual_error, create_renderer, 13 12 http::{ 14 13 context::{admin_template_context, AdminRequestContext}, 15 14 errors::WebError, 16 15 pagination::{Pagination, PaginationView}, 17 16 }, 18 - select_template, 19 17 storage::handle::{handle_list, handle_nuke}, 20 18 }; 21 19 ··· 27 25 "https://{}/admin/handles", 28 26 admin_ctx.web_context.config.external_base 29 27 ); 30 - let default_context = admin_template_context(&admin_ctx, &canonical_url); 31 - 32 - let render_template = select_template!("admin_handles", false, false, admin_ctx.language); 33 - let error_template = select_template!(false, false, admin_ctx.language); 28 + 29 + // Create the template renderer with enhanced context 30 + let renderer = create_renderer!(admin_ctx.web_context.clone(), admin_ctx.language, false, false); 34 31 35 32 let (page, page_size) = pagination.admin_clamped(); 36 33 37 34 let handles = handle_list(&admin_ctx.web_context.pool, page, page_size).await; 38 35 if let Err(err) = handles { 39 - return contextual_error!( 40 - admin_ctx.web_context, 41 - admin_ctx.language, 42 - error_template, 43 - default_context, 44 - err 45 - ); 36 + return contextual_error!(renderer: renderer, err, template_context!{}); 46 37 } 47 38 let (total_count, mut handles) = handles.unwrap(); 48 39 ··· 54 45 handles.truncate(page_size as usize); 55 46 } 56 47 57 - Ok(RenderHtml( 58 - &render_template, 59 - admin_ctx.web_context.engine.clone(), 60 - template_context! { ..default_context, ..template_context! { 48 + Ok(renderer.render_template( 49 + "admin_handles", 50 + template_context! { 61 51 handles, 62 52 total_count, 63 53 pagination => pagination_view, 64 - }}, 65 - ) 66 - .into_response()) 54 + }, 55 + Some(&admin_ctx.admin_handle), 56 + &canonical_url, 57 + )?) 67 58 } 68 59 69 60 pub async fn handle_admin_nuke_identity(
+12 -18
src/http/handle_admin_rsvp.rs
··· 4 4 response::IntoResponse, 5 5 }; 6 6 use axum_extra::extract::Cached; 7 - use axum_template::RenderHtml; 8 7 use minijinja::context as template_context; 9 8 use serde::Deserialize; 10 9 11 10 use crate::{ 12 - contextual_error, 11 + contextual_error, create_renderer, 13 12 http::{ 14 13 context::WebContext, errors::WebError, middleware_auth::Auth, middleware_i18n::Language, 15 14 }, 16 - select_template, 17 15 storage::event::rsvp_get, 18 16 }; 19 17 ··· 30 28 ) -> Result<impl IntoResponse, WebError> { 31 29 let current_handle = auth.require_admin(&web_context.config)?; 32 30 33 - let default_context = template_context! { 34 - language => language.to_string(), 35 - current_handle, 36 - canonical_url => format!("https://{}/admin/rsvp", web_context.config.external_base), 37 - }; 31 + let canonical_url = format!("https://{}/admin/rsvp", web_context.config.external_base); 38 32 39 - let render_template = select_template!("admin_rsvp", false, false, language); 40 - let error_template = select_template!(false, false, language); 33 + // Create the template renderer with enhanced context 34 + let renderer = create_renderer!(web_context.clone(), language, false, false); 41 35 42 36 // Fetch the RSVP 43 37 let rsvp_result = rsvp_get(&web_context.pool, &query.aturi).await; 44 38 if let Err(err) = rsvp_result { 45 - return contextual_error!(web_context, language, error_template, default_context, err); 39 + return contextual_error!(renderer: renderer, err, template_context!{ aturi => query.aturi.clone() }); 46 40 } 47 41 let rsvp = rsvp_result.unwrap(); 48 42 49 43 // Convert the RSVP to a JSON string for display 50 44 let rsvp_json = serde_json::to_string_pretty(&rsvp).unwrap_or_default(); 51 45 52 - Ok(RenderHtml( 53 - &render_template, 54 - web_context.engine.clone(), 55 - template_context! { ..default_context, ..template_context! { 46 + Ok(renderer.render_template( 47 + "admin_rsvp", 48 + template_context! { 56 49 rsvp, 57 50 rsvp_json, 58 - }}, 59 - ) 60 - .into_response()) 51 + }, 52 + Some(&current_handle), 53 + &canonical_url, 54 + )?) 61 55 }
+9 -29
src/http/handle_admin_rsvps.rs
··· 1 1 use anyhow::Result; 2 2 use axum::{extract::Query, response::IntoResponse}; 3 - use axum_template::RenderHtml; 4 3 use minijinja::context as template_context; 5 4 use serde::Deserialize; 6 5 7 6 use crate::{ 8 - contextual_error, 7 + contextual_error, create_renderer, 9 8 http::{ 10 9 context::AdminRequestContext, 11 10 errors::WebError, 12 11 pagination::{Pagination, PaginationView}, 13 12 }, 14 - select_template, 15 13 storage::event::rsvp_list, 16 14 }; 17 15 ··· 37 35 38 36 let canonical_url = format!("https://{}/admin/rsvps", web_context.config.external_base); 39 37 40 - // Create the context with all needed parameters 41 - let default_context = template_context! { 42 - language => language.to_string(), 43 - current_handle => admin_ctx.admin_handle.clone(), 44 - canonical_url => canonical_url, 45 - import_success => import_success, 46 - imported_aturi => imported_aturi, 47 - }; 48 - 49 - let render_template = select_template!("admin_rsvps", false, false, language); 50 - let error_template = select_template!(false, false, language); 38 + // Create the template renderer with enhanced context 39 + let renderer = create_renderer!(web_context.clone(), language, false, false); 51 40 52 41 let (page, page_size) = params.pagination.admin_clamped(); 53 42 54 43 let rsvps = rsvp_list(&web_context.pool, page, page_size).await; 55 44 if let Err(err) = rsvps { 56 - return contextual_error!( 57 - web_context, 58 - language.0, 59 - error_template, 60 - default_context, 61 - err 62 - ); 45 + return contextual_error!(renderer: renderer, err, template_context!{}); 63 46 } 64 47 let (total_count, mut rsvps) = rsvps.unwrap(); 65 48 ··· 71 54 rsvps.truncate(page_size as usize); 72 55 } 73 56 74 - Ok(RenderHtml( 75 - &render_template, 76 - web_context.engine.clone(), 57 + Ok(renderer.render_template( 58 + "admin_rsvps", 77 59 template_context! { 78 - language => language.to_string(), 79 - current_handle => admin_ctx.admin_handle.clone(), 80 - canonical_url => canonical_url, 81 60 import_success => import_success, 82 61 imported_aturi => imported_aturi, 83 62 rsvps => rsvps, 84 63 total_count => total_count, 85 64 pagination => pagination_view, 86 65 }, 87 - ) 88 - .into_response()) 66 + Some(&admin_ctx.admin_handle), 67 + &canonical_url, 68 + )?) 89 69 }
+85 -93
src/http/handle_create_event.rs
··· 9 9 use axum_extra::extract::Form; 10 10 use axum_htmx::HxBoosted; 11 11 use axum_htmx::HxRequest; 12 - use axum_template::RenderHtml; 13 12 use chrono::Utc; 14 13 use http::Method; 15 14 use http::StatusCode; ··· 26 25 use crate::atproto::lexicon::community::lexicon::calendar::event::Status; 27 26 use crate::atproto::lexicon::community::lexicon::calendar::event::NSID; 28 27 use crate::atproto::lexicon::community::lexicon::location::Address; 29 - use crate::contextual_error; 28 + use crate::create_renderer; 30 29 use crate::http::context::WebContext; 30 + use crate::http::template_renderer::TemplateRenderer; 31 31 use crate::http::errors::CommonError; 32 32 use crate::http::errors::CreateEventError; 33 33 use crate::http::errors::WebError; ··· 39 39 use crate::http::middleware_i18n::Language; 40 40 use crate::http::timezones::supported_timezones; 41 41 use crate::http::utils::url_from_aturi; 42 - use crate::select_template; 43 42 use crate::storage::event::event_insert; 44 43 45 44 use super::cache_countries::cached_countries; ··· 56 55 ) -> Result<impl IntoResponse, WebError> { 57 56 let current_handle = auth.require(&web_context.config.destination_key, "/event")?; 58 57 58 + // Create the template renderer with enhanced context 59 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, hx_request); 60 + 61 + let canonical_url = format!("https://{}/event", web_context.config.external_base); 59 62 let is_development = cfg!(debug_assertions); 60 63 61 64 let default_context = template_context! { 62 - current_handle, 63 - language => language.to_string(), 64 - canonical_url => format!("https://{}/event", web_context.config.external_base), 65 65 is_development, 66 66 create_event => true, 67 67 submit_url => format!("/event"), 68 68 }; 69 - // <a href="/{{ handle_slug }}/{{ event_rkey }}" class="button">Cancel</a> 70 - 71 - let render_template = select_template!("create_event", hx_boosted, hx_request, language); 72 - 73 - let error_template = select_template!(hx_boosted, hx_request, language); 74 69 75 70 let (default_tz, timezones) = supported_timezones(auth.0.as_ref()); 76 71 ··· 142 137 build_event_form.starts_at = starts_form.starts_at.clone(); 143 138 } 144 139 145 - return Ok(RenderHtml( 146 - &render_template, 147 - web_context.engine.clone(), 148 - template_context! { ..default_context, ..template_context! { 140 + return Ok(renderer.render_template( 141 + "create_event", 142 + template_context! { 143 + ..default_context, 149 144 build_event_form, 150 145 starts_form, 151 146 location_form, 152 147 link_form, 153 148 timezones, 154 - }}, 155 - ) 156 - .into_response()); 149 + }, 150 + Some(&current_handle.handle), 151 + &canonical_url, 152 + )); 157 153 } 158 154 159 155 match build_event_form.build_state { ··· 279 275 let create_record_result = client.create_record(&client_auth, event_record).await; 280 276 281 277 if let Err(err) = create_record_result { 282 - return contextual_error!( 283 - web_context, 284 - language, 285 - error_template, 286 - default_context, 287 - err 288 - ); 278 + return Ok(renderer.render_error(err, default_context)); 289 279 } 290 280 291 281 // create_record_result is guaranteed to be Ok since we checked for Err above ··· 302 292 .await; 303 293 304 294 if let Err(err) = event_insert_result { 305 - return contextual_error!( 306 - web_context, 307 - language, 308 - error_template, 309 - default_context, 310 - err 311 - ); 295 + return Ok(renderer.render_error(err, default_context)); 312 296 } 313 297 314 298 let event_url = 315 299 url_from_aturi(&web_context.config.external_base, &create_record_result.uri)?; 316 300 317 - return Ok(RenderHtml( 318 - &render_template, 319 - web_context.engine.clone(), 320 - template_context! { ..default_context, ..template_context! { 301 + return Ok(renderer.render_template( 302 + "create_event", 303 + template_context! { 304 + ..default_context, 321 305 build_event_form, 322 306 starts_form, 323 307 location_form, 324 308 link_form, 325 309 operation_completed => true, 326 310 event_url, 327 - }}, 328 - ) 329 - .into_response()); 311 + }, 312 + Some(&current_handle.handle), 313 + &canonical_url, 314 + )); 330 315 } 331 316 } 332 317 _ => {} 333 318 } 334 319 335 - Ok(RenderHtml( 336 - &render_template, 337 - web_context.engine.clone(), 338 - template_context! { ..default_context, ..template_context! { 320 + Ok(renderer.render_template( 321 + "create_event", 322 + template_context! { 323 + ..default_context, 339 324 build_event_form, 340 325 starts_form, 341 326 timezones, 342 327 location_form, 343 328 link_form, 344 - }}, 345 - ) 346 - .into_response()) 329 + }, 330 + Some(&current_handle.handle), 331 + &canonical_url, 332 + )) 347 333 } 348 334 349 335 pub async fn handle_starts_at_builder( ··· 366 352 367 353 let is_development = cfg!(debug_assertions); 368 354 369 - let render_template = format!( 370 - "create_event.{}.starts_form.html", 371 - language.to_string().to_lowercase() 372 - ); 355 + // Create the template renderer for HTMX partial updates 356 + let renderer = create_renderer!(web_context.clone(), Language(language.clone()), false, true); 357 + 358 + let canonical_url = format!("https://{}/event", web_context.config.external_base); 373 359 374 360 if starts_form.build_state.is_none() { 375 361 starts_form.build_state = Some(BuildEventContentState::default()); ··· 379 365 } 380 366 381 367 if method == Method::GET { 382 - return Ok(RenderHtml( 383 - &render_template, 384 - web_context.engine.clone(), 368 + return Ok(renderer.render_template( 369 + "create_event.starts_form", 385 370 template_context! { 386 371 starts_form, 387 372 is_development, 388 373 timezones, 389 374 }, 390 - ) 391 - .into_response()); 375 + None, 376 + &canonical_url, 377 + )); 392 378 } 393 379 394 380 if starts_form ··· 424 410 } 425 411 } 426 412 427 - Ok(RenderHtml( 428 - &render_template, 429 - web_context.engine.clone(), 413 + Ok(renderer.render_template( 414 + "create_event.starts_form", 430 415 template_context! { 431 416 starts_form, 432 417 is_development, 433 418 timezones, 434 419 }, 435 - ) 436 - .into_response()) 420 + None, 421 + &canonical_url, 422 + )) 437 423 } 438 424 439 425 pub async fn handle_location_at_builder( ··· 454 440 455 441 let is_development = cfg!(debug_assertions); 456 442 457 - let render_template = format!( 458 - "create_event.{}.location_form.html", 459 - language.to_string().to_lowercase() 460 - ); 443 + // Create the template renderer for HTMX partial updates 444 + let renderer = create_renderer!(web_context.clone(), Language(language.clone()), false, true); 445 + 446 + let canonical_url = format!("https://{}/event", web_context.config.external_base); 461 447 462 448 if location_form.build_state.is_none() { 463 449 location_form.build_state = Some(BuildEventContentState::default()); 464 450 } 465 451 466 452 if method == Method::GET { 467 - return Ok(RenderHtml( 468 - &render_template, 469 - web_context.engine.clone(), 453 + return Ok(renderer.render_template( 454 + "create_event.location_form", 470 455 template_context! { 471 456 location_form, 472 457 is_development 473 458 }, 474 - ) 475 - .into_response()); 459 + None, 460 + &canonical_url, 461 + )); 476 462 } 477 463 478 464 if location_form ··· 499 485 } 500 486 } 501 487 502 - Ok(RenderHtml( 503 - &render_template, 504 - web_context.engine.clone(), 488 + Ok(renderer.render_template( 489 + "create_event.location_form", 505 490 template_context! { 506 491 location_form, 507 492 is_development, 508 493 }, 509 - ) 510 - .into_response()) 494 + None, 495 + &canonical_url, 496 + )) 511 497 } 512 498 513 499 pub async fn handle_link_at_builder( ··· 528 514 529 515 let is_development = cfg!(debug_assertions); 530 516 531 - let render_template = format!( 532 - "create_event.{}.link_form.html", 533 - language.to_string().to_lowercase() 534 - ); 517 + // Create the template renderer for HTMX partial updates 518 + let renderer = create_renderer!(web_context.clone(), Language(language.clone()), false, true); 519 + 520 + let canonical_url = format!("https://{}/event", web_context.config.external_base); 535 521 536 522 if link_form.build_state.is_none() { 537 523 link_form.build_state = Some(BuildEventContentState::default()); 538 524 } 539 525 540 526 if method == Method::GET { 541 - return Ok(RenderHtml( 542 - &render_template, 543 - web_context.engine.clone(), 527 + return Ok(renderer.render_template( 528 + "create_event.link_form", 544 529 template_context! { 545 530 link_form, 546 531 is_development 547 532 }, 548 - ) 549 - .into_response()); 533 + None, 534 + &canonical_url, 535 + )); 550 536 } 551 537 552 538 if link_form ··· 573 559 } 574 560 } 575 561 576 - Ok(RenderHtml( 577 - &render_template, 578 - web_context.engine.clone(), 562 + Ok(renderer.render_template( 563 + "create_event.link_form", 579 564 template_context! { 580 565 link_form, 581 566 is_development, 582 567 }, 583 - ) 584 - .into_response()) 568 + None, 569 + &canonical_url, 570 + )) 585 571 } 586 572 587 573 #[derive(Deserialize, Debug, Clone)] ··· 591 577 592 578 pub async fn handle_location_datalist( 593 579 State(web_context): State<WebContext>, 580 + Language(language): Language, 594 581 HxRequest(hx_request): HxRequest, 595 582 Query(location_country_hint): Query<LocationDataListHint>, 596 583 ) -> Result<impl IntoResponse, WebError> { ··· 598 585 return Ok(StatusCode::BAD_REQUEST.into_response()); 599 586 } 600 587 588 + // Create the template renderer for HTMX partial updates 589 + let renderer = create_renderer!(web_context.clone(), Language(language), false, true); 590 + 591 + let canonical_url = format!("https://{}/event", web_context.config.external_base); 592 + 601 593 let all_countries = cached_countries()?; 602 594 603 595 let locations = if let Some(value) = location_country_hint.location_country { ··· 614 606 .collect::<Vec<(String, String)>>() 615 607 }; 616 608 617 - Ok(RenderHtml( 618 - "create_event.countries_datalist.html", 619 - web_context.engine.clone(), 609 + Ok(renderer.render_template( 610 + "create_event.countries_datalist", 620 611 template_context! { 621 612 locations, 622 613 }, 623 - ) 624 - .into_response()) 614 + None, 615 + &canonical_url, 616 + )) 625 617 } 626 618 627 619 // Nick: The next two methods were adapted from https://www.thecodedmessage.com/posts/prefix-ranges/ which has no license. Thank you.
+28 -48
src/http/handle_create_rsvp.rs
··· 2 2 use axum::{extract::State, response::IntoResponse}; 3 3 use axum_extra::extract::{Cached, Form}; 4 4 use axum_htmx::{HxBoosted, HxRequest}; 5 - use axum_template::RenderHtml; 6 5 use chrono::Utc; 7 6 use http::Method; 8 7 use metrohash::MetroHash64; ··· 18 17 community::lexicon::calendar::rsvp::{Rsvp, RsvpStatus, NSID}, 19 18 }, 20 19 }, 21 - contextual_error, 20 + contextual_error, create_renderer, 22 21 http::{ 23 22 context::WebContext, 24 23 errors::WebError, ··· 27 26 rsvp_form::{BuildRSVPForm, BuildRsvpContentState}, 28 27 utils::url_from_aturi, 29 28 }, 30 - select_template, 31 29 storage::event::rsvp_insert, 32 30 }; 33 31 ··· 42 40 ) -> Result<impl IntoResponse, WebError> { 43 41 let current_handle = auth.require(&web_context.config.destination_key, "/rsvp")?; 44 42 45 - let default_context = template_context! { 46 - current_handle, 47 - language => language.to_string(), 48 - canonical_url => format!("https://{}/rsvp", web_context.config.external_base), 49 - hx_request, 50 - hx_boosted, 51 - }; 52 - 53 - let render_template = select_template!("create_rsvp", hx_boosted, hx_request, language); 54 - let error_template = select_template!(hx_boosted, hx_request, language); 43 + // Create the template renderer with enhanced context 44 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, hx_request); 45 + 46 + let canonical_url = format!("https://{}/rsvp", web_context.config.external_base); 55 47 56 48 if build_rsvp_form.build_state.is_none() { 57 49 build_rsvp_form.build_state = Some(BuildRsvpContentState::default()); ··· 65 57 66 58 build_rsvp_form.build_state = Some(BuildRsvpContentState::Selecting); 67 59 68 - return Ok(RenderHtml( 69 - &render_template, 70 - web_context.engine.clone(), 71 - template_context! { ..default_context, ..template_context! { 60 + return Ok(renderer.render_template( 61 + "create_rsvp", 62 + template_context! { 72 63 build_rsvp_form, 73 - }}, 74 - ) 75 - .into_response()); 64 + }, 65 + Some(&current_handle.handle), 66 + &canonical_url, 67 + )); 76 68 } 77 69 78 70 match build_rsvp_form.build_state { ··· 159 151 let put_record_result = client.put_record(&client_auth, rsvp_record).await; 160 152 161 153 if let Err(err) = put_record_result { 162 - return contextual_error!( 163 - web_context, 164 - language, 165 - error_template, 166 - default_context, 167 - err 168 - ); 154 + return contextual_error!(renderer: renderer, err, template_context!{}); 169 155 } 170 156 171 157 let create_record_result = put_record_result.unwrap(); ··· 181 167 .await; 182 168 183 169 if let Err(err) = rsvp_insert_result { 184 - return contextual_error!( 185 - web_context, 186 - language, 187 - error_template, 188 - default_context, 189 - err 190 - ); 170 + return contextual_error!(renderer: renderer, err, template_context!{}); 191 171 } 192 172 193 173 let event_url = url_from_aturi( ··· 195 175 build_rsvp_form.subject_aturi.clone().unwrap().as_str(), 196 176 )?; 197 177 198 - return Ok(RenderHtml( 199 - &render_template, 200 - web_context.engine.clone(), 201 - template_context! { ..default_context, ..template_context! { 178 + return Ok(renderer.render_template( 179 + "create_rsvp", 180 + template_context! { 202 181 build_rsvp_form, 203 182 event_url, 204 - }}, 205 - ) 206 - .into_response()); 183 + }, 184 + Some(&current_handle.handle), 185 + &canonical_url, 186 + )); 207 187 } 208 188 } 209 189 None => unreachable!(), 210 190 } 211 191 212 - Ok(RenderHtml( 213 - &render_template, 214 - web_context.engine.clone(), 215 - template_context! { ..default_context, ..template_context! { 192 + Ok(renderer.render_template( 193 + "create_rsvp", 194 + template_context! { 216 195 build_rsvp_form 217 - }}, 218 - ) 219 - .into_response()) 196 + }, 197 + Some(&current_handle.handle), 198 + &canonical_url, 199 + )) 220 200 }
+53 -68
src/http/handle_edit_event.rs
··· 2 2 use axum::{extract::Path, response::IntoResponse}; 3 3 use axum_extra::extract::Form; 4 4 use axum_htmx::{HxBoosted, HxRequest}; 5 - use axum_template::RenderHtml; 6 5 use chrono::Utc; 7 6 use http::{Method, StatusCode}; 8 7 use minijinja::context as template_context; ··· 27 26 http::timezones::supported_timezones, 28 27 http::utils::url_from_aturi, 29 28 resolve::{parse_input, InputType}, 30 - select_template, 29 + create_renderer, 31 30 storage::{ 32 31 event::{event_get, event_update_with_metadata}, 33 32 handle::{handle_for_did, handle_for_handle}, ··· 46 45 .auth 47 46 .require(&ctx.web_context.config.destination_key, "/")?; 48 47 49 - let default_context = template_context! { 50 - current_handle, 51 - language => ctx.language.to_string(), 52 - canonical_url => format!("https://{}/{}/{}/edit", ctx.web_context.config.external_base, handle_slug, event_rkey), 53 - create_event => false, 54 - submit_url => format!("/{}/{}/edit", handle_slug, event_rkey), 55 - cancel_url => format!("/{}/{}", handle_slug, event_rkey), 56 - }; 48 + let canonical_url = format!( 49 + "https://{}/{}/{}/edit", 50 + ctx.web_context.config.external_base, handle_slug, event_rkey 51 + ); 57 52 58 - let render_template = select_template!("edit_event", hx_boosted, hx_request, ctx.language); 59 - let error_template = select_template!(hx_boosted, hx_request, ctx.language); 53 + // Create the template renderer with enhanced context 54 + let renderer = create_renderer!(ctx.web_context.clone(), ctx.language, hx_boosted, hx_request); 60 55 61 56 // Lookup the event 62 57 let profile = match parse_input(&handle_slug) { ··· 79 74 // Check if the user is authorized to edit this event (must be the creator) 80 75 if profile.did != current_handle.did { 81 76 return contextual_error!( 82 - ctx.web_context, 83 - ctx.language, 84 - error_template, 85 - default_context, 77 + renderer: renderer, 86 78 EditEventError::NotAuthorized, 79 + template_context!{}, 87 80 StatusCode::FORBIDDEN 88 81 ); 89 82 } 90 83 91 84 let event = event_get(&ctx.web_context.pool, &lookup_aturi).await; 92 85 if let Err(err) = event { 93 - return contextual_error!( 94 - ctx.web_context, 95 - ctx.language, 96 - error_template, 97 - default_context, 98 - err, 99 - StatusCode::OK 100 - ); 86 + return contextual_error!(renderer: renderer, err, template_context!{}, StatusCode::OK); 101 87 } 102 88 103 89 let event = event.unwrap(); ··· 105 91 // Check if this is a community calendar event (we only support editing those) 106 92 if event.lexicon != LexiconCommunityEventNSID { 107 93 return contextual_error!( 108 - ctx.web_context, 109 - ctx.language, 110 - error_template, 111 - default_context, 94 + renderer: renderer, 112 95 EditEventError::UnsupportedEventType, 96 + template_context!{}, 113 97 StatusCode::BAD_REQUEST 114 98 ); 115 99 } ··· 120 104 Ok(event) => event, 121 105 Err(_) => { 122 106 return contextual_error!( 123 - ctx.web_context, 124 - ctx.language, 125 - error_template, 126 - default_context, 107 + renderer: renderer, 127 108 CommonError::InvalidEventFormat, 109 + template_context!{}, 128 110 StatusCode::BAD_REQUEST 129 111 ); 130 112 } ··· 385 367 386 368 return Ok(( 387 369 StatusCode::OK, 388 - RenderHtml( 389 - &render_template, 390 - ctx.web_context.engine.clone(), 391 - template_context! { ..default_context, ..template_context! { 370 + renderer.render_template( 371 + "edit_event", 372 + template_context! { 392 373 build_event_form, 393 374 starts_form, 394 375 location_form, ··· 400 381 locations_editable, 401 382 location_edit_reason, 402 383 location_display_info, 403 - }}, 404 - ), 405 - ) 406 - .into_response()); 384 + create_event => false, 385 + submit_url => format!("/{}/{}/edit", handle_slug, event_rkey), 386 + cancel_url => format!("/{}/{}", handle_slug, event_rkey), 387 + }, 388 + Some(&current_handle), 389 + &canonical_url, 390 + )?, 391 + )); 407 392 } 408 393 409 394 // Process form state changes just like in create_event ··· 522 507 }; 523 508 524 509 return contextual_error!( 525 - ctx.web_context, 526 - ctx.language, 527 - error_template, 528 - default_context, 510 + renderer: renderer, 529 511 error, 512 + template_context!{}, 530 513 StatusCode::BAD_REQUEST 531 514 ); 532 515 } ··· 606 589 607 590 if let Err(err) = update_record_result { 608 591 return contextual_error!( 609 - ctx.web_context, 610 - ctx.language, 611 - error_template, 612 - default_context, 592 + renderer: renderer, 613 593 err, 594 + template_context!{}, 614 595 StatusCode::OK 615 596 ); 616 597 } ··· 633 614 634 615 if let Err(err) = event_update_result { 635 616 return contextual_error!( 636 - ctx.web_context, 637 - ctx.language, 638 - error_template, 639 - default_context, 617 + renderer: renderer, 640 618 err, 619 + template_context!{}, 641 620 StatusCode::OK 642 621 ); 643 622 } ··· 647 626 648 627 return Ok(( 649 628 StatusCode::OK, 650 - RenderHtml( 651 - &render_template, 652 - ctx.web_context.engine.clone(), 653 - template_context! { ..default_context, ..template_context! { 629 + renderer.render_template( 630 + "edit_event", 631 + template_context! { 654 632 build_event_form, 655 633 starts_form, 656 634 location_form, ··· 663 641 is_development, 664 642 locations_editable, 665 643 location_edit_reason, 666 - }}, 667 - ), 668 - ) 669 - .into_response()); 644 + create_event => false, 645 + submit_url => format!("/{}/{}/edit", handle_slug, event_rkey), 646 + cancel_url => format!("/{}/{}", handle_slug, event_rkey), 647 + }, 648 + Some(&current_handle), 649 + &canonical_url, 650 + )?, 651 + )); 670 652 } 671 653 } 672 654 _ => {} ··· 674 656 675 657 Ok(( 676 658 StatusCode::OK, 677 - RenderHtml( 678 - &render_template, 679 - ctx.web_context.engine.clone(), 680 - template_context! { ..default_context, ..template_context! { 659 + renderer.render_template( 660 + "edit_event", 661 + template_context! { 681 662 build_event_form, 682 663 starts_form, 683 664 location_form, ··· 688 669 is_development, 689 670 locations_editable, 690 671 location_edit_reason, 691 - }}, 692 - ), 693 - ) 694 - .into_response()) 672 + create_event => false, 673 + submit_url => format!("/{}/{}/edit", handle_slug, event_rkey), 674 + cancel_url => format!("/{}/{}", handle_slug, event_rkey), 675 + }, 676 + Some(&current_handle), 677 + &canonical_url, 678 + )?, 679 + )) 695 680 }
+40 -54
src/http/handle_import.rs
··· 4 4 }; 5 5 use axum_extra::extract::Cached; 6 6 use axum_htmx::{HxBoosted, HxRequest}; 7 - use axum_template::RenderHtml; 8 7 use http::StatusCode; 9 8 use minijinja::context as template_context; 10 9 use serde::Deserialize; ··· 30 29 }, 31 30 }, 32 31 }, 33 - contextual_error, 32 + contextual_error, create_renderer, 34 33 http::{ 35 34 context::WebContext, 36 35 errors::{ImportError, WebError}, 37 36 middleware_auth::Auth, 38 37 middleware_i18n::Language, 39 38 }, 40 - select_template, 41 39 storage::event::{event_insert_with_metadata, rsvp_insert_with_metadata}, 42 40 }; 43 41 ··· 50 48 ) -> Result<impl IntoResponse, WebError> { 51 49 let current_handle = auth.require(&web_context.config.destination_key, "/import")?; 52 50 53 - let default_context = template_context! { 54 - current_handle, 55 - language => language.to_string(), 56 - canonical_url => format!("https://{}/import", web_context.config.external_base), 57 - }; 58 - 59 - let render_template = select_template!("import", hx_boosted, hx_request, language); 51 + // Create the template renderer with enhanced context 52 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, hx_request); 53 + 54 + let canonical_url = format!("https://{}/import", web_context.config.external_base); 60 55 61 - Ok(RenderHtml( 62 - &render_template, 63 - web_context.engine.clone(), 64 - default_context, 65 - ) 66 - .into_response()) 56 + Ok(renderer.render_template( 57 + "import", 58 + template_context! {}, 59 + Some(&current_handle.handle), 60 + &canonical_url, 61 + )) 67 62 } 68 63 69 64 #[derive(Debug, Deserialize)] ··· 85 80 return Ok(StatusCode::BAD_REQUEST.into_response()); 86 81 } 87 82 88 - let render_template = select_template!("import", false, true, language); 89 - let error_template = select_template!(false, hx_request, language); 83 + // Create the template renderer for HTMX partial updates 84 + let renderer = create_renderer!(web_context.clone(), Language(language), false, true); 85 + 86 + let canonical_url = format!("https://{}/import", web_context.config.external_base); 90 87 91 88 let collections = [ 92 89 LEXICON_COMMUNITY_EVENT_NSID, ··· 166 163 } 167 164 Err(err) => { 168 165 return contextual_error!( 169 - web_context, 170 - language, 171 - error_template, 172 - template_context! {}, 173 - ImportError::FailedToListCommunityEvents(err.to_string()) 166 + renderer: renderer, 167 + ImportError::FailedToListCommunityEvents(err.to_string()), 168 + template_context!{} 174 169 ) 175 170 } 176 171 } ··· 238 233 } 239 234 Err(err) => { 240 235 return contextual_error!( 241 - web_context, 242 - language, 243 - error_template, 244 - template_context! {}, 245 - ImportError::FailedToListCommunityRSVPs(err.to_string()) 236 + renderer: renderer, 237 + ImportError::FailedToListCommunityRSVPs(err.to_string()), 238 + template_context!{} 246 239 ) 247 240 } 248 241 } ··· 296 289 } 297 290 Err(err) => { 298 291 return contextual_error!( 299 - web_context, 300 - language, 301 - error_template, 302 - template_context! {}, 303 - ImportError::FailedToListSmokesignalEvents(err.to_string()) 292 + renderer: renderer, 293 + ImportError::FailedToListSmokesignalEvents(err.to_string()), 294 + template_context!{} 304 295 ) 305 296 } 306 297 } ··· 364 355 } 365 356 Err(err) => { 366 357 return contextual_error!( 367 - web_context, 368 - language, 369 - error_template, 370 - template_context! {}, 371 - ImportError::FailedToListSmokesignalRSVPs(err.to_string()) 358 + renderer: renderer, 359 + ImportError::FailedToListSmokesignalRSVPs(err.to_string()), 360 + template_context!{} 372 361 ) 373 362 } 374 363 } 375 364 } 376 365 _ => { 377 366 return contextual_error!( 378 - web_context, 379 - language, 380 - error_template, 381 - template_context! {}, 382 - ImportError::UnsupportedCollectionType(collection.clone()) 367 + renderer: renderer, 368 + ImportError::UnsupportedCollectionType(collection.clone()), 369 + template_context!{} 383 370 ) 384 371 } 385 372 }; 386 373 387 - Ok(RenderHtml( 388 - &render_template, 389 - web_context.engine.clone(), 390 - template_context! { 391 - current_handle, 392 - language => language.to_string(), 393 - canonical_url => format!("https://{}/import", web_context.config.external_base), 394 - ..render_context, 395 - }, 396 - ) 397 - .into_response()) 374 + Ok(renderer 375 + .render_template( 376 + "import", 377 + template_context! { 378 + ..render_context, 379 + }, 380 + Some(&current_handle.handle), 381 + &canonical_url, 382 + ) 383 + .into_response()) 398 384 }
+10 -18
src/http/handle_index.rs
··· 7 7 }; 8 8 use axum_extra::extract::Cached; 9 9 use axum_htmx::HxBoosted; 10 - use axum_template::RenderHtml; 11 10 12 11 use minijinja::context as template_context; 13 12 use serde::{Deserialize, Serialize}; 14 13 15 14 use crate::{ 16 - contextual_error, 15 + contextual_error, create_renderer, 17 16 http::{ 18 17 context::WebContext, 19 18 errors::WebError, ··· 23 22 pagination::{Pagination, PaginationView}, 24 23 tab_selector::TabSelector, 25 24 }, 26 - select_template, 27 25 storage::event::event_list_recently_updated, 28 26 }; 29 27 ··· 54 52 pagination: Query<Pagination>, 55 53 tab_selector: Query<TabSelector>, 56 54 ) -> Result<impl IntoResponse, WebError> { 57 - let render_template = select_template!("index", hx_boosted, false, language); 58 - let error_template = select_template!(false, false, language); 55 + // Create the template renderer with enhanced context 56 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 57 + 58 + let canonical_url = format!("https://{}/", web_context.config.external_base); 59 59 60 60 let (page, page_size) = pagination.clamped(); 61 61 let tab: HomeTab = tab_selector.0.into(); ··· 70 70 match tab_events { 71 71 Ok(values) => values, 72 72 Err(err) => { 73 - return contextual_error!( 74 - web_context, 75 - language, 76 - error_template, 77 - template_context! {}, 78 - err 79 - ); 73 + return contextual_error!(renderer: renderer, err, template_context!{}); 80 74 } 81 75 } 82 76 }; ··· 114 108 115 109 Ok(( 116 110 http::StatusCode::OK, 117 - RenderHtml( 118 - &render_template, 119 - web_context.engine.clone(), 111 + renderer.render_template( 112 + "index", 120 113 template_context! { 121 - current_handle => auth.0, 122 - language => language.to_string(), 123 - canonical_url => format!("https://{}/", web_context.config.external_base), 124 114 tab => tab.to_string(), 125 115 events, 126 116 pagination => pagination_view, 127 117 }, 118 + auth.0.as_ref().map(|h| h.handle.as_str()), 119 + &canonical_url, 128 120 ), 129 121 ) 130 122 .into_response())
+70 -83
src/http/handle_migrate_event.rs
··· 5 5 }; 6 6 use axum_extra::extract::Cached; 7 7 use axum_htmx::{HxBoosted, HxRequest}; 8 - use axum_template::RenderHtml; 9 8 use http::StatusCode; 10 9 use minijinja::context as template_context; 11 10 use std::collections::HashMap; ··· 26 25 }, 27 26 }, 28 27 }, 29 - contextual_error, 28 + create_renderer, 30 29 http::{ 31 - context::WebContext, errors::MigrateEventError, errors::WebError, middleware_auth::Auth, 32 - middleware_i18n::Language, utils::url_from_aturi, 30 + context::WebContext, 31 + errors::{MigrateEventError, WebError}, 32 + middleware_auth::Auth, 33 + middleware_i18n::Language, 34 + template_renderer::TemplateRenderer, 35 + utils::url_from_aturi, 33 36 }, 34 37 resolve::{parse_input, InputType}, 35 - select_template, 36 38 storage::{ 37 39 event::{event_get, event_insert_with_metadata}, 38 40 handle::{handle_for_did, handle_for_handle, model::Handle}, ··· 49 51 ) -> Result<impl IntoResponse, WebError> { 50 52 let current_handle = auth.require(&web_context.config.destination_key, "/")?; 51 53 52 - // Configure templates 53 - let default_context = template_context! { 54 - current_handle, 55 - language => language.to_string(), 56 - canonical_url => format!("https://{}/{}/{}/migrate", web_context.config.external_base, handle_slug, event_rkey), 57 - }; 58 - let render_template = select_template!("migrate_event", hx_boosted, hx_request, language); 59 - let error_template = select_template!(hx_boosted, hx_request, language); 54 + // Create the template renderer with enhanced context 55 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, hx_request); 56 + let canonical_url = format!("https://{}/{}/{}/migrate", renderer.web_context.config.external_base, handle_slug, event_rkey); 60 57 61 58 // Lookup the user handle/profile 62 59 let profile: Result<Handle> = match parse_input(&handle_slug) { ··· 70 67 }; 71 68 72 69 if let Err(err) = profile { 73 - return contextual_error!( 74 - web_context, 75 - language, 76 - error_template, 77 - default_context, 70 + return Ok(renderer.render_error( 78 71 err, 79 - StatusCode::NOT_FOUND 80 - ); 72 + template_context! { 73 + current_handle => current_handle.handle.as_str(), 74 + canonical_url => canonical_url, 75 + }, 76 + )); 81 77 } 82 78 let profile = profile.unwrap(); 83 79 ··· 86 82 87 83 // Check if the user is authorized to migrate this event (must be the event creator/organizer) 88 84 if profile.did != current_handle.did { 89 - return contextual_error!( 90 - web_context, 91 - language, 92 - error_template, 93 - default_context, 85 + return Ok(renderer.render_error( 94 86 MigrateEventError::NotAuthorized, 95 - StatusCode::FORBIDDEN 96 - ); 87 + template_context! { 88 + current_handle => current_handle.handle.as_str(), 89 + canonical_url => canonical_url, 90 + }, 91 + )); 97 92 } 98 93 99 94 // Fetch the event from the database 100 95 let event = event_get(&web_context.pool, &source_aturi).await; 101 96 if let Err(err) = event { 102 - return contextual_error!( 103 - web_context, 104 - language, 105 - error_template, 106 - default_context, 97 + return Ok(renderer.render_error( 107 98 err, 108 - StatusCode::OK 109 - ); 99 + template_context! { 100 + current_handle => current_handle.handle.as_str(), 101 + canonical_url => canonical_url, 102 + }, 103 + )); 110 104 } 111 105 let event = event.unwrap(); 112 106 ··· 114 108 if event.lexicon != SMOKESIGNAL_NSID { 115 109 // If it's already the community event type, we don't need to migrate 116 110 if event.lexicon == COMMUNITY_NSID { 117 - return contextual_error!( 118 - web_context, 119 - language, 120 - error_template, 121 - default_context, 111 + return Ok(renderer.render_error( 122 112 MigrateEventError::AlreadyMigrated, 123 - StatusCode::BAD_REQUEST 124 - ); 113 + template_context! { 114 + current_handle => current_handle.handle.as_str(), 115 + canonical_url => canonical_url, 116 + }, 117 + )); 125 118 } 126 119 127 - return contextual_error!( 128 - web_context, 129 - language, 130 - error_template, 131 - default_context, 120 + return Ok(renderer.render_error( 132 121 MigrateEventError::UnsupportedEventType, 133 - StatusCode::BAD_REQUEST 134 - ); 122 + template_context! { 123 + current_handle => current_handle.handle.as_str(), 124 + canonical_url => canonical_url, 125 + }, 126 + )); 135 127 } 136 128 137 129 // Parse the legacy event 138 130 let legacy_event = match serde_json::from_value::<SmokeSignalEvent>(event.record.0.clone()) { 139 131 Ok(event) => event, 140 132 Err(err) => { 141 - return contextual_error!( 142 - web_context, 143 - language, 144 - error_template, 145 - default_context, 133 + return Ok(renderer.render_error( 146 134 err, 147 - StatusCode::BAD_REQUEST 148 - ); 135 + template_context! { 136 + current_handle => current_handle.handle.as_str(), 137 + canonical_url => canonical_url, 138 + }, 139 + )); 149 140 } 150 141 }; 151 142 ··· 251 242 // Check if a record already exists at the target AT-URI 252 243 let existing_event = event_get(&web_context.pool, &migrated_aturi).await; 253 244 if existing_event.is_ok() { 254 - return contextual_error!( 255 - web_context, 256 - language, 257 - error_template, 258 - default_context, 245 + return Ok(renderer.render_error( 259 246 MigrateEventError::DestinationExists, 260 - StatusCode::CONFLICT 261 - ); 247 + template_context! { 248 + current_handle => current_handle.handle.as_str(), 249 + canonical_url => canonical_url, 250 + }, 251 + )); 262 252 } 263 253 264 254 // Set up XRPC client ··· 285 275 // Write to the PDS 286 276 let update_record_result = client.put_record(&client_auth, update_record_request).await; 287 277 if let Err(err) = update_record_result { 288 - return contextual_error!( 289 - web_context, 290 - language, 291 - error_template, 292 - default_context, 278 + return Ok(renderer.render_error( 293 279 err, 294 - StatusCode::OK 295 - ); 280 + template_context! { 281 + current_handle => current_handle.handle.as_str(), 282 + canonical_url => canonical_url, 283 + }, 284 + )); 296 285 } 297 286 // update_record_result is guaranteed to be Ok at this point since we checked for Err above 298 - let update_record_result = update_record_result?; 287 + let update_record_result = update_record_result.unwrap(); 299 288 300 289 // We already have the migrated AT-URI defined above 301 290 ··· 312 301 .await; 313 302 314 303 if let Err(err) = migrated_event_insert_result { 315 - return contextual_error!( 316 - web_context, 317 - language, 318 - error_template, 319 - default_context, 304 + return Ok(renderer.render_error( 320 305 err, 321 - StatusCode::OK 322 - ); 306 + template_context! { 307 + current_handle => current_handle.handle.as_str(), 308 + canonical_url => canonical_url, 309 + }, 310 + )); 323 311 } 324 312 325 313 // Generate URL for the migrated event 326 314 let migrated_event_url = url_from_aturi(&web_context.config.external_base, &migrated_aturi)?; 327 315 328 316 // Return success with migration complete template 329 - Ok(RenderHtml( 330 - &render_template, 331 - web_context.engine.clone(), 317 + Ok(renderer.render_template( 318 + "migrate_event", 332 319 template_context! { 333 320 migrated_event_url => migrated_event_url, 334 321 source_aturi => source_aturi, ··· 336 323 event_name => name, 337 324 source_lexicon => SMOKESIGNAL_NSID, 338 325 target_lexicon => COMMUNITY_NSID, 339 - ..default_context 340 326 }, 341 - ) 342 - .into_response()) 327 + Some(current_handle.handle.as_str()), 328 + &canonical_url, 329 + )) 343 330 }
+43 -58
src/http/handle_oauth_login.rs
··· 3 3 use axum::{extract::State, response::IntoResponse}; 4 4 use axum_extra::extract::{Cached, Form, Query}; 5 5 use axum_htmx::{HxBoosted, HxRedirect, HxRequest}; 6 - use axum_template::RenderHtml; 7 6 use base64::{engine::general_purpose, Engine as _}; 8 7 use http::StatusCode; 9 8 use minijinja::context as template_context; ··· 14 13 use std::borrow::Cow; 15 14 16 15 use crate::{ 17 - contextual_error, 16 + contextual_error, create_renderer, 18 17 did::{plc::query as plc_query, web::query as web_query}, 19 18 http::{ 20 19 context::WebContext, errors::LoginError, errors::WebError, middleware_auth::Auth, ··· 23 22 jose, 24 23 oauth::{oauth_init, pds_resources}, 25 24 resolve::{parse_input, resolve_subject, InputType}, 26 - select_template, 27 25 storage::{ 28 26 denylist::denylist_exists, 29 27 handle::handle_warm_up, ··· 51 49 Query(destination): Query<Destination>, 52 50 Form(login_form): Form<OAuthLoginForm>, 53 51 ) -> Result<impl IntoResponse, WebError> { 52 + // Create the template renderer with enhanced context 53 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, hx_request); 54 + 55 + let canonical_url = format!("https://{}/oauth/login", web_context.config.external_base); 56 + let current_handle = auth.0.as_ref().map(|h| h.handle.as_str()).unwrap_or(""); 57 + 54 58 let default_context = template_context! { 55 - current_handle => auth.0, 56 - language => language.to_string(), 57 - canonical_url => format!("https://{}/oauth/login", web_context.config.external_base), 58 59 destination => destination.destination, 59 60 }; 60 61 61 - let render_template = select_template!("login", hx_boosted, hx_request, language); 62 - let error_template = select_template!(hx_boosted, hx_request, language); 63 - 64 62 if let Some(subject) = login_form.handle { 65 63 let resolved_did = resolve_subject( 66 64 &web_context.http_client, ··· 70 68 .await; 71 69 72 70 if let Err(err) = resolved_did { 73 - return contextual_error!( 74 - web_context, 75 - language, 76 - render_template, 77 - template_context! { ..default_context.clone(), ..template_context! { 78 - handle_error => true, 79 - handle_input => subject, 80 - }}, 81 - err 82 - ); 71 + let error_context = template_context! { 72 + ..default_context.clone(), 73 + handle_error => true, 74 + handle_input => subject, 75 + }; 76 + return contextual_error!(renderer: renderer, err, error_context); 83 77 } 84 78 85 79 let resolved_did = resolved_did.unwrap(); ··· 100 94 let did_document = match query_results { 101 95 Ok(value) => value, 102 96 Err(err) => { 103 - return contextual_error!( 104 - web_context, 105 - language, 106 - render_template, 107 - template_context! { ..default_context.clone(), ..template_context! { 108 - handle_error => true, 109 - handle_input => subject, 110 - }}, 111 - err 112 - ); 97 + let error_context = template_context! { 98 + ..default_context.clone(), 99 + handle_error => true, 100 + handle_input => subject, 101 + }; 102 + return contextual_error!(renderer: renderer, err, error_context); 113 103 } 114 104 }; 115 105 ··· 147 137 let pds = match did_document.pds_endpoint() { 148 138 Some(value) => value, 149 139 None => { 150 - return contextual_error!( 151 - web_context, 152 - language, 153 - render_template, 154 - template_context! { ..default_context.clone(), ..template_context! { 155 - handle_error => true, 156 - handle_input => subject, 157 - }}, 158 - LoginError::NoPDS 159 - ); 140 + let error_context = template_context! { 141 + ..default_context.clone(), 142 + handle_error => true, 143 + handle_input => subject, 144 + }; 145 + return contextual_error!(renderer: renderer, LoginError::NoPDS, error_context); 160 146 } 161 147 }; 162 148 163 149 let primary_handle = match did_document.primary_handle() { 164 150 Some(value) => value, 165 151 None => { 166 - return contextual_error!( 167 - web_context, 168 - language, 169 - render_template, 170 - template_context! { ..default_context.clone(), ..template_context! { 171 - handle_error => true, 172 - handle_input => subject, 173 - }}, 174 - LoginError::NoHandle 175 - ); 152 + let error_context = template_context! { 153 + ..default_context.clone(), 154 + handle_error => true, 155 + handle_input => subject, 156 + }; 157 + return contextual_error!(renderer: renderer, LoginError::NoHandle, error_context); 176 158 } 177 159 }; 178 160 179 161 if let Err(err) = 180 162 handle_warm_up(&web_context.pool, &did_document.id, primary_handle, pds).await 181 163 { 182 - return contextual_error!(web_context, language, error_template, default_context, err); 164 + return contextual_error!(renderer: renderer, err, default_context); 183 165 } 184 166 185 167 let state: String = rand::thread_rng() ··· 296 278 return Ok(Redirect::temporary(destination.as_str()).into_response()); 297 279 } 298 280 299 - Ok(RenderHtml( 300 - &render_template, 301 - web_context.engine.clone(), 302 - template_context! { ..default_context, ..template_context! { 303 - destination => destination.destination, 304 - }}, 305 - ) 306 - .into_response()) 281 + let final_context = template_context! { 282 + ..default_context, 283 + destination => destination.destination, 284 + }; 285 + 286 + Ok(renderer.render_template( 287 + "login", 288 + final_context, 289 + Some(current_handle), 290 + &canonical_url, 291 + )) 307 292 } 308 293 309 294 pub fn gen_pkce() -> (String, String) {
+16 -9
src/http/handle_oauth_logout.rs
··· 5 5 }; 6 6 use axum_extra::extract::{cookie::Cookie, PrivateCookieJar}; 7 7 use axum_htmx::{HxRedirect, HxRequest}; 8 - use axum_template::RenderHtml; 9 8 use http::StatusCode; 10 9 use minijinja::context as template_context; 11 10 12 - use crate::http::{ 13 - context::WebContext, errors::WebError, middleware_auth::AUTH_COOKIE_NAME, 14 - middleware_i18n::Language, 11 + use crate::{ 12 + create_renderer, 13 + http::{ 14 + context::WebContext, errors::WebError, middleware_auth::AUTH_COOKIE_NAME, 15 + middleware_i18n::Language, 16 + }, 15 17 }; 16 18 17 19 pub async fn handle_logout( ··· 26 28 let hx_redirect = HxRedirect::try_from("/"); 27 29 if let Err(err) = hx_redirect { 28 30 tracing::error!("Failed to create HxLocation: {}", err); 29 - return Ok(RenderHtml( 30 - format!("alert.{}.partial.html", language.to_string().to_lowercase()), 31 - web_context.engine.clone(), 31 + 32 + // Create the template renderer for error handling 33 + let renderer = create_renderer!(web_context.clone(), Language(language), false, true); 34 + let canonical_url = format!("https://{}/", web_context.config.external_base); 35 + 36 + return Ok(renderer.render_template( 37 + "alert.partial", 32 38 template_context! { message => "Internal Server Error" }, 33 - ) 34 - .into_response()); 39 + None, 40 + &canonical_url, 41 + )); 35 42 } 36 43 let hx_redirect = hx_redirect.unwrap(); 37 44 Ok((StatusCode::OK, hx_redirect, "").into_response())
+45 -38
src/http/handle_policy.rs
··· 2 2 use axum::{extract::State, response::IntoResponse}; 3 3 use axum_extra::extract::Cached; 4 4 use axum_htmx::HxBoosted; 5 - use axum_template::RenderHtml; 6 5 7 6 use minijinja::context as template_context; 8 7 9 8 use crate::{ 9 + create_renderer, 10 10 http::{ 11 11 context::WebContext, errors::WebError, middleware_auth::Auth, middleware_i18n::Language, 12 12 }, 13 - select_template, 14 13 }; 15 14 16 15 pub async fn handle_privacy_policy( ··· 19 18 Language(language): Language, 20 19 Cached(auth): Cached<Auth>, 21 20 ) -> Result<impl IntoResponse, WebError> { 22 - let render_template = select_template!("privacy-policy", hx_boosted, false, language); 21 + // Create the template renderer with enhanced context 22 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 23 + 24 + let canonical_url = format!("https://{}/privacy-policy", web_context.config.external_base); 25 + let current_handle = auth.0.as_ref().map(|h| h.handle.as_str()).unwrap_or(""); 26 + 23 27 Ok(( 24 28 http::StatusCode::OK, 25 - RenderHtml( 26 - &render_template, 27 - web_context.engine.clone(), 28 - template_context! { 29 - current_handle => auth.0, 30 - language => language.to_string(), 31 - canonical_url => format!("https://{}/privacy-policy", web_context.config.external_base), 32 - }, 29 + renderer.render_template( 30 + "privacy-policy", 31 + template_context! {}, 32 + Some(current_handle), 33 + &canonical_url, 33 34 ), 34 35 ) 35 36 .into_response()) ··· 41 42 Language(language): Language, 42 43 Cached(auth): Cached<Auth>, 43 44 ) -> Result<impl IntoResponse, WebError> { 44 - let render_template = select_template!("terms-of-service", hx_boosted, false, language); 45 + // Create the template renderer with enhanced context 46 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 47 + 48 + let canonical_url = format!("https://{}/terms-of-service", web_context.config.external_base); 49 + let current_handle = auth.0.as_ref().map(|h| h.handle.as_str()).unwrap_or(""); 50 + 45 51 Ok(( 46 52 http::StatusCode::OK, 47 - RenderHtml( 48 - &render_template, 49 - web_context.engine.clone(), 50 - template_context! { 51 - current_handle => auth.0, 52 - language => language.to_string(), 53 - canonical_url => format!("https://{}/terms-of-service", web_context.config.external_base), 54 - }, 53 + renderer.render_template( 54 + "terms-of-service", 55 + template_context! {}, 56 + Some(current_handle), 57 + &canonical_url, 55 58 ), 56 59 ) 57 60 .into_response()) ··· 63 66 Language(language): Language, 64 67 Cached(auth): Cached<Auth>, 65 68 ) -> Result<impl IntoResponse, WebError> { 66 - let render_template = select_template!("cookie-policy", hx_boosted, false, language); 69 + // Create the template renderer with enhanced context 70 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 71 + 72 + let canonical_url = format!("https://{}/cookie-policy", web_context.config.external_base); 73 + let current_handle = auth.0.as_ref().map(|h| h.handle.as_str()).unwrap_or(""); 74 + 67 75 Ok(( 68 76 http::StatusCode::OK, 69 - RenderHtml( 70 - &render_template, 71 - web_context.engine.clone(), 72 - template_context! { 73 - current_handle => auth.0, 74 - language => language.to_string(), 75 - canonical_url => format!("https://{}/cookie-policy", web_context.config.external_base), 76 - }, 77 + renderer.render_template( 78 + "cookie-policy", 79 + template_context! {}, 80 + Some(current_handle), 81 + &canonical_url, 77 82 ), 78 83 ) 79 84 .into_response()) ··· 85 90 Language(language): Language, 86 91 Cached(auth): Cached<Auth>, 87 92 ) -> Result<impl IntoResponse, WebError> { 88 - let render_template = select_template!("acknowledgement", hx_boosted, false, language); 93 + // Create the template renderer with enhanced context 94 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 95 + 96 + let canonical_url = format!("https://{}/acknowledgement", web_context.config.external_base); 97 + let current_handle = auth.0.as_ref().map(|h| h.handle.as_str()).unwrap_or(""); 98 + 89 99 Ok(( 90 100 http::StatusCode::OK, 91 - RenderHtml( 92 - &render_template, 93 - web_context.engine.clone(), 94 - template_context! { 95 - current_handle => auth.0, 96 - language => language.to_string(), 97 - canonical_url => format!("https://{}/acknowledgement", web_context.config.external_base), 98 - }, 101 + renderer.render_template( 102 + "acknowledgement", 103 + template_context! {}, 104 + Some(current_handle), 105 + &canonical_url, 99 106 ), 100 107 ) 101 108 .into_response())
+20 -27
src/http/handle_profile.rs
··· 3 3 use axum::response::IntoResponse; 4 4 use axum_extra::extract::Query; 5 5 use axum_htmx::{HxBoosted, HxRequest}; 6 - use axum_template::RenderHtml; 7 6 use chrono_tz::Tz; 8 7 use http::StatusCode; 9 8 use minijinja::context as template_context; ··· 11 10 use std::fmt; 12 11 13 12 use crate::{ 14 - contextual_error, 13 + contextual_error, create_renderer, 15 14 http::{ 16 15 context::UserRequestContext, 17 16 errors::{CommonError, WebError}, ··· 20 19 tab_selector::{TabLink, TabSelector}, 21 20 utils::build_url, 22 21 }, 23 - select_template, 24 22 storage::{ 25 23 errors::StorageError, 26 24 event::{event_list_did_recently_updated, model::EventWithRole}, ··· 57 55 pagination: Query<Pagination>, 58 56 tab_selector: Query<TabSelector>, 59 57 ) -> Result<impl IntoResponse, WebError> { 60 - let default_context = template_context! { 61 - language => ctx.language.to_string(), 62 - current_handle => ctx.current_handle, 63 - }; 64 - 65 - let render_template = select_template!("profile", hx_boosted, hx_request, ctx.language); 66 - let error_template = select_template!(false, hx_request, ctx.language); 58 + let renderer = create_renderer!(ctx.web_context, ctx.language, hx_boosted, hx_request); 67 59 68 60 if !handle_slug.starts_with("did:web:") 69 61 && !handle_slug.starts_with("did:plc:") 70 62 && !handle_slug.starts_with('@') 71 63 { 72 64 return contextual_error!( 73 - ctx.web_context, 74 - ctx.language, 75 - error_template, 76 - default_context, 65 + renderer: renderer, 77 66 CommonError::InvalidHandleSlug, 67 + template_context!{}, 78 68 StatusCode::NOT_FOUND 79 69 ); 80 70 } ··· 91 81 92 82 if let Err(err) = profile { 93 83 return contextual_error!( 94 - ctx.web_context, 95 - ctx.language, 96 - error_template, 97 - default_context, 84 + renderer: renderer, 98 85 err, 86 + template_context!{}, 99 87 StatusCode::NOT_FOUND 100 88 ); 101 89 } ··· 142 130 Ok(values) => values, 143 131 Err(err) => { 144 132 return contextual_error!( 145 - ctx.web_context, 146 - ctx.language, 147 - error_template, 148 - default_context, 133 + renderer: renderer, 149 134 err, 135 + template_context!{}, 150 136 StatusCode::NOT_FOUND 151 137 ); 152 138 } ··· 193 179 active: tab == ProfileTab::RecentlyUpdated, 194 180 }]; 195 181 182 + let canonical_url = format!( 183 + "https://{}/{}", 184 + ctx.web_context.config.external_base, 185 + handle_slug 186 + ); 187 + 196 188 Ok(( 197 189 StatusCode::OK, 198 - RenderHtml( 199 - &render_template, 200 - ctx.web_context.engine.clone(), 201 - template_context! { ..default_context, ..template_context! { 190 + renderer.render_template( 191 + "profile", 192 + template_context! { 202 193 tab => tab.to_string(), 203 194 tabs => tab_links, 204 195 events, 205 196 pagination => pagination_view, 206 - }}, 197 + }, 198 + ctx.current_handle.as_ref().map(|h| &h.handle), 199 + &canonical_url, 207 200 ), 208 201 ) 209 202 .into_response())
+33 -67
src/http/handle_settings.rs
··· 2 2 use axum::{extract::State, response::IntoResponse}; 3 3 use axum_extra::extract::{Cached, Form}; 4 4 use axum_htmx::HxBoosted; 5 - use axum_template::RenderHtml; 6 5 use http::StatusCode; 7 6 use minijinja::context as template_context; 8 7 use serde::Deserialize; ··· 10 9 use unic_langid::LanguageIdentifier; 11 10 12 11 use crate::{ 13 - contextual_error, 12 + contextual_error, create_renderer, 14 13 http::{ 15 14 context::WebContext, errors::WebError, middleware_auth::Auth, middleware_i18n::Language, 16 15 timezones::supported_timezones, 17 16 }, 18 - select_template, 19 17 storage::handle::{handle_for_did, handle_update_field, HandleField}, 20 18 }; 21 19 ··· 38 36 // Require authentication 39 37 let current_handle = auth.require(&web_context.config.destination_key, "/settings")?; 40 38 41 - let default_context = template_context! { 42 - current_handle => current_handle.clone(), 43 - language => language.to_string(), 44 - canonical_url => format!("https://{}/settings", web_context.config.external_base), 45 - }; 46 - 47 - let render_template = select_template!("settings", hx_boosted, false, language); 39 + // Create the template renderer with enhanced context 40 + let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); 41 + 42 + let canonical_url = format!("https://{}/settings", web_context.config.external_base); 48 43 49 44 // Get available timezones 50 45 let (_, timezones) = supported_timezones(Some(&current_handle)); ··· 60 55 // Render the form 61 56 Ok(( 62 57 StatusCode::OK, 63 - RenderHtml( 64 - &render_template, 65 - web_context.engine.clone(), 58 + renderer.render_template( 59 + "settings", 66 60 template_context! { 67 61 timezones => timezones, 68 62 languages => supported_languages, 69 - ..default_context, 70 63 }, 64 + Some(&current_handle.handle), 65 + &canonical_url, 71 66 ), 72 67 ) 73 68 .into_response()) ··· 82 77 ) -> Result<impl IntoResponse, WebError> { 83 78 let current_handle = auth.require_flat()?; 84 79 85 - let default_context = template_context! { 86 - current_handle => current_handle.clone(), 87 - language => language.to_string(), 88 - }; 89 - 90 - let error_template = select_template!(false, true, language); 91 - let render_template = format!("settings.{}.tz.html", language.to_string().to_lowercase()); 80 + // Create the template renderer for partial updates 81 + let renderer = create_renderer!(web_context.clone(), Language(language), false, true); 82 + 83 + let canonical_url = format!("https://{}/settings", web_context.config.external_base); 92 84 93 85 let (_, timezones) = supported_timezones(Some(&current_handle)); 94 86 ··· 96 88 || timezone_form.timezone == current_handle.tz 97 89 || !timezones.contains(&timezone_form.timezone.as_str()) 98 90 { 99 - return contextual_error!( 100 - web_context, 101 - language, 102 - error_template, 103 - default_context, 104 - "error-xxx Invalid timezone" 105 - ); 91 + return contextual_error!(renderer: renderer, "Invalid timezone", template_context!{}); 106 92 } 107 93 108 94 if let Err(err) = handle_update_field( ··· 112 98 ) 113 99 .await 114 100 { 115 - return contextual_error!(web_context, language, error_template, default_context, err); 101 + return contextual_error!(renderer: renderer, err, template_context!{}); 116 102 } 117 103 118 104 let current_handle = match handle_for_did(&web_context.pool, &current_handle.did).await { 119 105 Ok(value) => value, 120 106 Err(err) => { 121 - return contextual_error!(web_context, language, error_template, default_context, err); 107 + return contextual_error!(renderer: renderer, err, template_context!{}); 122 108 } 123 109 }; 124 110 125 111 Ok(( 126 112 StatusCode::OK, 127 - RenderHtml( 128 - &render_template, 129 - web_context.engine.clone(), 113 + renderer.render_template( 114 + "settings.tz", 130 115 template_context! { 131 116 timezone_updated => true, 132 117 timezones, 133 - current_handle, 134 - ..default_context 135 118 }, 119 + Some(&current_handle.handle), 120 + &canonical_url, 136 121 ), 137 122 ) 138 123 .into_response()) ··· 147 132 ) -> Result<impl IntoResponse, WebError> { 148 133 let current_handle = auth.require_flat()?; 149 134 150 - let default_context = template_context! { 151 - current_handle => current_handle.clone(), 152 - language => language.to_string(), 153 - }; 154 - 155 - let error_template = select_template!(false, true, language); 156 - let render_template = format!( 157 - "settings.{}.language.html", 158 - language.to_string().to_lowercase() 159 - ); 135 + // Create the template renderer for partial updates 136 + let renderer = create_renderer!(web_context.clone(), Language(language), false, true); 137 + 138 + let canonical_url = format!("https://{}/settings", web_context.config.external_base); 160 139 161 140 // Get the list of supported languages 162 141 let supported_languages = web_context ··· 167 146 .collect::<Vec<String>>(); 168 147 169 148 if language_form.language.is_empty() || language_form.language == current_handle.language { 170 - return contextual_error!( 171 - web_context, 172 - language, 173 - error_template, 174 - default_context, 175 - "error-xxx Invalid language" 176 - ); 149 + return contextual_error!(renderer: renderer, "Invalid language", template_context!{}); 177 150 } 178 151 179 152 let lang_id = match language_form.language.parse::<LanguageIdentifier>() { 180 153 Ok(value) => value, 181 154 Err(err) => { 182 - return contextual_error!(web_context, language, error_template, default_context, err); 155 + return contextual_error!(renderer: renderer, err, template_context!{}); 183 156 } 184 157 }; 185 158 ··· 189 162 .iter() 190 163 .any(|l| l == &lang_id) 191 164 { 192 - return contextual_error!( 193 - web_context, 194 - language, 195 - error_template, 196 - default_context, 197 - "error-xxx Invalid language" 198 - ); 165 + return contextual_error!(renderer: renderer, "Invalid language", template_context!{}); 199 166 } 200 167 201 168 if let Err(err) = handle_update_field( ··· 205 172 ) 206 173 .await 207 174 { 208 - return contextual_error!(web_context, language, error_template, default_context, err); 175 + return contextual_error!(renderer: renderer, err, template_context!{}); 209 176 } 210 177 211 178 let current_handle = match handle_for_did(&web_context.pool, &current_handle.did).await { 212 179 Ok(value) => value, 213 180 Err(err) => { 214 - return contextual_error!(web_context, language, error_template, default_context, err); 181 + return contextual_error!(renderer: renderer, err, template_context!{}); 215 182 } 216 183 }; 217 184 218 185 Ok(( 219 186 StatusCode::OK, 220 - RenderHtml( 221 - &render_template, 222 - web_context.engine.clone(), 187 + renderer.render_template( 188 + "settings.lang", 223 189 template_context! { 224 - current_handle, 225 190 language_updated => true, 226 191 languages => supported_languages, 227 - ..default_context 228 192 }, 193 + Some(&current_handle.handle), 194 + &canonical_url, 229 195 ), 230 196 ) 231 197 .into_response())
+15 -33
src/http/handle_view_event.rs
··· 6 6 response::{IntoResponse, Redirect}, 7 7 }; 8 8 use axum_htmx::HxBoosted; 9 - use axum_template::RenderHtml; 10 9 use http::StatusCode; 11 10 use minijinja::context as template_context; 12 11 use serde::{Deserialize, Serialize}; ··· 14 13 use crate::atproto::lexicon::community::lexicon::calendar::event::NSID; 15 14 use crate::atproto::lexicon::events::smokesignal::calendar::event::NSID as SMOKESIGNAL_EVENT_NSID; 16 15 use crate::contextual_error; 16 + use crate::create_renderer; 17 17 use crate::http::context::UserRequestContext; 18 18 use crate::http::errors::CommonError; 19 19 use crate::http::errors::ViewEventError; ··· 25 25 use crate::http::utils::url_from_aturi; 26 26 use crate::resolve::parse_input; 27 27 use crate::resolve::InputType; 28 - use crate::select_template; 29 28 use crate::storage::event::count_event_rsvps; 30 29 use crate::storage::event::event_exists; 31 30 use crate::storage::event::event_get; ··· 93 92 tab_selector: Query<TabSelector>, 94 93 collection_param: Query<CollectionParam>, 95 94 ) -> Result<impl IntoResponse, WebError> { 96 - let default_context = template_context! { 97 - language => ctx.language.to_string(), 98 - current_handle => ctx.current_handle, 99 - }; 95 + let event_url = format!( 96 + "https://{}/event/{}/{}", 97 + ctx.web_context.config.external_base, handle_slug, event_rkey 98 + ); 100 99 101 - let render_template = select_template!("view_event", hx_boosted, false, ctx.language); 102 - let error_template = select_template!(hx_boosted, false, ctx.language); 100 + // Create the template renderer with enhanced context 101 + let renderer = create_renderer!(ctx.web_context.clone(), ctx.language, hx_boosted, false); 103 102 104 103 let profile: Result<Handle, WebError> = match parse_input(&handle_slug) { 105 104 Ok(InputType::Handle(handle)) => handle_for_handle(&ctx.web_context.pool, &handle) ··· 114 113 }; 115 114 116 115 if let Err(err) = profile { 117 - return contextual_error!( 118 - ctx.web_context, 119 - ctx.language, 120 - error_template, 121 - default_context, 122 - err, 123 - StatusCode::NOT_FOUND 124 - ); 116 + return contextual_error!(renderer: renderer, err, template_context!{}, StatusCode::NOT_FOUND); 125 117 } 126 118 127 119 let profile = profile.unwrap(); ··· 244 236 } 245 237 246 238 if let Err(err) = event_result { 247 - return contextual_error!( 248 - ctx.web_context, 249 - ctx.language, 250 - error_template, 251 - default_context, 252 - err, 253 - StatusCode::NOT_FOUND 254 - ); 239 + return contextual_error!(renderer: renderer, err, template_context!{}, StatusCode::NOT_FOUND); 255 240 } 256 241 257 242 let mut event = event_result.unwrap(); ··· 449 434 450 435 Ok(( 451 436 StatusCode::OK, 452 - RenderHtml( 453 - &render_template, 454 - ctx.web_context.engine.clone(), 437 + renderer.render_template( 438 + "view_event", 455 439 template_context! { 456 - current_handle => ctx.current_handle, 457 - language => ctx.language.to_string(), 458 - canonical_url => event_url, 459 440 event => event_with_counts, 460 441 is_self, 461 442 can_edit, ··· 479 460 SMOKESIGNAL_EVENT_NSID => SMOKESIGNAL_EVENT_NSID, 480 461 using_SMOKESIGNAL_EVENT_NSID => collection == SMOKESIGNAL_EVENT_NSID, 481 462 }, 482 - ), 483 - ) 484 - .into_response()) 463 + ctx.current_handle.as_ref(), 464 + &event_url, 465 + )?, 466 + )) 485 467 } 486 468 487 469 #[cfg(test)]