i18n+filtering fork - fluent-templates v2
at main 200 lines 6.8 kB view raw
1use anyhow::Result; 2use axum::{extract::State, response::IntoResponse}; 3use axum_extra::extract::{Cached, Form}; 4use axum_htmx::{HxBoosted, HxRequest}; 5use chrono::Utc; 6use http::Method; 7use metrohash::MetroHash64; 8use minijinja::context as template_context; 9use std::hash::Hasher; 10 11use crate::{ 12 atproto::{ 13 auth::SimpleOAuthSessionProvider, 14 client::{OAuthPdsClient, PutRecordRequest}, 15 lexicon::{ 16 com::atproto::repo::StrongRef, 17 community::lexicon::calendar::rsvp::{Rsvp, RsvpStatus, NSID}, 18 }, 19 }, 20 contextual_error, create_renderer, 21 http::{ 22 context::WebContext, 23 errors::WebError, 24 middleware_auth::Auth, 25 middleware_i18n::Language, 26 rsvp_form::{BuildRSVPForm, BuildRsvpContentState}, 27 utils::url_from_aturi, 28 }, 29 storage::event::rsvp_insert, 30}; 31 32pub async fn handle_create_rsvp( 33 method: Method, 34 State(web_context): State<WebContext>, 35 Language(language): Language, 36 Cached(auth): Cached<Auth>, 37 HxRequest(hx_request): HxRequest, 38 HxBoosted(hx_boosted): HxBoosted, 39 Form(mut build_rsvp_form): Form<BuildRSVPForm>, 40) -> Result<impl IntoResponse, WebError> { 41 let current_handle = auth.require(&web_context.config.destination_key, "/rsvp")?; 42 43 // Create the template renderer with enhanced context 44 let renderer = create_renderer!(web_context.clone(), Language(language.clone()), hx_boosted, hx_request); 45 46 let canonical_url = format!("https://{}/rsvp", web_context.config.external_base); 47 48 if build_rsvp_form.build_state.is_none() { 49 build_rsvp_form.build_state = Some(BuildRsvpContentState::default()); 50 } 51 52 if method == Method::GET { 53 #[cfg(debug_assertions)] 54 { 55 build_rsvp_form.status = Some("going".to_string()); 56 } 57 58 build_rsvp_form.build_state = Some(BuildRsvpContentState::Selecting); 59 60 return Ok(renderer.render_template( 61 "create_rsvp", 62 template_context! { 63 build_rsvp_form, 64 }, 65 Some(&current_handle), 66 &canonical_url, 67 )); 68 } 69 70 match build_rsvp_form.build_state { 71 Some(BuildRsvpContentState::Reset) => { 72 build_rsvp_form.build_state = Some(BuildRsvpContentState::Selecting); 73 build_rsvp_form.subject_aturi = None; 74 build_rsvp_form.subject_cid = None; 75 build_rsvp_form.status = Some("going".to_string()); 76 } 77 Some(BuildRsvpContentState::Selecting) => {} 78 Some(BuildRsvpContentState::Selected) => { 79 build_rsvp_form 80 .hydrate( 81 &web_context.pool, 82 &web_context.i18n_context.locales, 83 &language, 84 ) 85 .await; 86 87 let found_errors = 88 build_rsvp_form.validate(&web_context.i18n_context.locales, &language); 89 if found_errors { 90 build_rsvp_form.build_state = Some(BuildRsvpContentState::Selecting); 91 } else { 92 build_rsvp_form.build_state = Some(BuildRsvpContentState::Selected); 93 } 94 } 95 Some(BuildRsvpContentState::Review) => { 96 build_rsvp_form 97 .hydrate( 98 &web_context.pool, 99 &web_context.i18n_context.locales, 100 &language, 101 ) 102 .await; 103 104 let found_errors = 105 build_rsvp_form.validate(&web_context.i18n_context.locales, &language); 106 107 if !found_errors { 108 let now = Utc::now(); 109 110 let client_auth: SimpleOAuthSessionProvider = 111 SimpleOAuthSessionProvider::try_from(auth.1.unwrap())?; 112 113 let client = OAuthPdsClient { 114 http_client: &web_context.http_client, 115 pds: &current_handle.pds, 116 }; 117 118 let subject = StrongRef { 119 uri: build_rsvp_form.subject_aturi.as_ref().unwrap().to_string(), 120 cid: build_rsvp_form.subject_cid.as_ref().unwrap().to_string(), 121 }; 122 123 let status = match build_rsvp_form.status.as_ref().unwrap().as_str() { 124 "going" => RsvpStatus::Going, 125 "interested" => RsvpStatus::Interested, 126 "notgoing" => RsvpStatus::NotGoing, 127 _ => unreachable!(), 128 }; 129 130 let mut h = MetroHash64::default(); 131 h.write(subject.uri.clone().as_bytes()); 132 133 let record_key = crockford::encode(h.finish()); 134 135 let the_record = Rsvp::Current { 136 created_at: now, 137 subject, 138 status, 139 }; 140 141 let rsvp_record = PutRecordRequest { 142 repo: current_handle.did.clone(), 143 collection: NSID.to_string(), 144 validate: false, 145 record_key, 146 record: the_record.clone(), 147 swap_commit: None, 148 swap_record: None, 149 }; 150 151 let put_record_result = client.put_record(&client_auth, rsvp_record).await; 152 153 if let Err(err) = put_record_result { 154 return contextual_error!(renderer: renderer, err, template_context!{}); 155 } 156 157 let create_record_result = put_record_result.unwrap(); 158 159 let rsvp_insert_result = rsvp_insert( 160 &web_context.pool, 161 &create_record_result.uri, 162 &create_record_result.cid, 163 &current_handle.did, 164 NSID, 165 &the_record, 166 ) 167 .await; 168 169 if let Err(err) = rsvp_insert_result { 170 return contextual_error!(renderer: renderer, err, template_context!{}); 171 } 172 173 let event_url = url_from_aturi( 174 &web_context.config.external_base, 175 build_rsvp_form.subject_aturi.clone().unwrap().as_str(), 176 )?; 177 178 return Ok(renderer.render_template( 179 "create_rsvp", 180 template_context! { 181 build_rsvp_form, 182 event_url, 183 }, 184 Some(&current_handle), 185 &canonical_url, 186 )); 187 } 188 } 189 None => unreachable!(), 190 } 191 192 Ok(renderer.render_template( 193 "create_rsvp", 194 template_context! { 195 build_rsvp_form 196 }, 197 Some(&current_handle), 198 &canonical_url, 199 )) 200}