use anyhow::Result; use axum::{extract::State, response::IntoResponse}; use axum_extra::extract::{Cached, Form}; use axum_htmx::HxBoosted; use http::StatusCode; use minijinja::context as template_context; use serde::Deserialize; use std::borrow::Cow; use unic_langid::LanguageIdentifier; use crate::{ contextual_error, create_renderer, http::{ context::WebContext, errors::WebError, middleware_auth::Auth, middleware_i18n::Language, timezones::supported_timezones, }, storage::handle::{handle_for_did, handle_update_field, HandleField}, }; #[derive(Deserialize, Clone, Debug)] pub struct TimezoneForm { timezone: String, } #[derive(Deserialize, Clone, Debug)] pub struct LanguageForm { language: String, } pub async fn handle_settings( State(web_context): State, Language(language): Language, Cached(auth): Cached, HxBoosted(hx_boosted): HxBoosted, ) -> Result { // Require authentication let current_handle = auth.require(&web_context.config.destination_key, "/settings")?; // Create the template renderer with enhanced context let renderer = create_renderer!(web_context.clone(), Language(language), hx_boosted, false); let canonical_url = format!("https://{}/settings", web_context.config.external_base); // Get available timezones let (_, timezones) = supported_timezones(Some(¤t_handle)); // Get the list of supported languages let supported_languages = web_context .i18n_context .supported_languages .iter() .map(|lang| lang.to_string()) .collect::>(); // Render the form Ok(( StatusCode::OK, renderer.render_template( "settings", template_context! { timezones => timezones, languages => supported_languages, }, Some(¤t_handle), &canonical_url, ), ) .into_response()) } #[tracing::instrument(skip_all, err)] pub async fn handle_timezone_update( State(web_context): State, Language(language): Language, Cached(auth): Cached, Form(timezone_form): Form, ) -> Result { let current_handle = auth.require_flat()?; // Create the template renderer for HTMX content replacement let renderer = create_renderer!(web_context.clone(), Language(language), false, false); let canonical_url = format!("https://{}/settings", web_context.config.external_base); let (_, timezones) = supported_timezones(Some(¤t_handle)); if timezone_form.timezone.is_empty() || timezone_form.timezone == current_handle.tz || !timezones.contains(&timezone_form.timezone.as_str()) { return contextual_error!(renderer: renderer, "Invalid timezone", template_context!{}); } if let Err(err) = handle_update_field( &web_context.pool, ¤t_handle.did, HandleField::Timezone(Cow::Owned(timezone_form.timezone)), ) .await { return contextual_error!(renderer: renderer, err, template_context!{}); } let current_handle = match handle_for_did(&web_context.pool, ¤t_handle.did).await { Ok(value) => value, Err(err) => { return contextual_error!(renderer: renderer, err, template_context!{}); } }; Ok(( StatusCode::OK, renderer.render_template( "settings.tz", template_context! { timezone_updated => true, timezones, }, Some(¤t_handle), &canonical_url, ), ) .into_response()) } #[tracing::instrument(skip_all, err)] pub async fn handle_language_update( State(web_context): State, Language(language): Language, Cached(auth): Cached, Form(language_form): Form, ) -> Result { let current_handle = auth.require_flat()?; // Create the template renderer for HTMX content replacement let renderer = create_renderer!(web_context.clone(), Language(language), false, false); let canonical_url = format!("https://{}/settings", web_context.config.external_base); // Get the list of supported languages let supported_languages = web_context .i18n_context .supported_languages .iter() .map(|lang| lang.to_string()) .collect::>(); if language_form.language.is_empty() || language_form.language == current_handle.language { return contextual_error!(renderer: renderer, "Invalid language", template_context!{}); } let lang_id = match language_form.language.parse::() { Ok(value) => value, Err(err) => { return contextual_error!(renderer: renderer, err, template_context!{}); } }; if !web_context .i18n_context .supported_languages .iter() .any(|l| l == &lang_id) { return contextual_error!(renderer: renderer, "Invalid language", template_context!{}); } if let Err(err) = handle_update_field( &web_context.pool, ¤t_handle.did, HandleField::Language(Cow::Owned(language_form.language)), ) .await { return contextual_error!(renderer: renderer, err, template_context!{}); } let current_handle = match handle_for_did(&web_context.pool, ¤t_handle.did).await { Ok(value) => value, Err(err) => { return contextual_error!(renderer: renderer, err, template_context!{}); } }; // Create a new renderer with the updated language for proper template selection let new_renderer = create_renderer!(web_context.clone(), Language(lang_id), false, false); Ok(( StatusCode::OK, new_renderer.render_template( "settings.language", template_context! { language_updated => true, languages => supported_languages, }, Some(¤t_handle), &canonical_url, ), ) .into_response()) }