forked from
smokesignal.events/smokesignal
i18n+filtering fork - fluent-templates v2
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(¤t_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: ¤t_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 ¤t_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(¤t_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(¤t_handle),
198 &canonical_url,
199 ))
200}