use db::sqlx; use model::users::UserAppearanceModel; use serde::{Deserialize, Serialize}; use session::Session; use crate::{AppService, error::AppError, session_user}; #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct UserAppearanceConfig { pub theme: String, pub code_theme: String, pub layout_density: String, pub sidebar_collapsed: bool, pub show_line_numbers: bool, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct UpdateUserAppearanceConfig { pub theme: Option, pub code_theme: Option, pub layout_density: Option, pub sidebar_collapsed: Option, pub show_line_numbers: Option, } impl AppService { pub async fn user_update_appearance_config( &self, ctx: &Session, params: UpdateUserAppearanceConfig, ) -> Result { let user_uid = session_user(ctx)?; let mut config = self.user_appearance_config(user_uid).await?; if let Some(theme) = params.theme { config.theme = theme; } if let Some(code_theme) = params.code_theme { config.code_theme = code_theme; } if let Some(layout_density) = params.layout_density { config.layout_density = layout_density; } if let Some(sidebar_collapsed) = params.sidebar_collapsed { config.sidebar_collapsed = sidebar_collapsed; } if let Some(show_line_numbers) = params.show_line_numbers { config.show_line_numbers = show_line_numbers; } let now = chrono::Utc::now(); sqlx::query( "INSERT INTO user_appearance \ (\"user\", theme, code_theme, layout_density, sidebar_collapsed, show_line_numbers, created_at, updated_at) \ VALUES ($1, $2, $3, $4, $5, $6, $7, $7) \ ON CONFLICT (\"user\") DO UPDATE SET \ theme = EXCLUDED.theme, code_theme = EXCLUDED.code_theme, layout_density = EXCLUDED.layout_density, \ sidebar_collapsed = EXCLUDED.sidebar_collapsed, show_line_numbers = EXCLUDED.show_line_numbers, \ updated_at = EXCLUDED.updated_at", ) .bind(user_uid) .bind(&config.theme) .bind(&config.code_theme) .bind(&config.layout_density) .bind(config.sidebar_collapsed) .bind(config.show_line_numbers) .bind(now) .execute(self.db.writer()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(config) } pub async fn user_appearance_config( &self, user_uid: uuid::Uuid, ) -> Result { let row = sqlx::query_as::<_, UserAppearanceModel>( "SELECT \"user\", theme, code_theme, layout_density, sidebar_collapsed, show_line_numbers, created_at, updated_at \ FROM user_appearance WHERE \"user\" = $1", ) .bind(user_uid) .fetch_optional(self.db.reader()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(row.map(Into::into).unwrap_or_default()) } } impl Default for UserAppearanceConfig { fn default() -> Self { Self { theme: "system".to_string(), code_theme: "github-dark".to_string(), layout_density: "comfortable".to_string(), sidebar_collapsed: false, show_line_numbers: true, } } } impl From for UserAppearanceConfig { fn from(value: UserAppearanceModel) -> Self { Self { theme: value.theme, code_theme: value.code_theme, layout_density: value.layout_density, sidebar_collapsed: value.sidebar_collapsed, show_line_numbers: value.show_line_numbers, } } }