use serde::{Deserialize, Serialize}; use crate::error::AppError; use crate::models::common::DigestFrequency; use crate::models::users::UserNotifySetting; use crate::service::UserService; use crate::session::Session; use super::util::parse_enum; #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct UpdateUserNotifySettingParams { pub email_notifications: Option, pub web_notifications: Option, pub mention_notifications: Option, pub review_notifications: Option, pub security_notifications: Option, pub marketing_emails: Option, pub digest_frequency: Option, } impl UserService { pub async fn user_notify_setting(&self, ctx: &Session) -> Result { let user_uid = ctx.user().ok_or(AppError::Unauthorized)?; self.ensure_user_notify_setting(user_uid).await } pub async fn user_update_notify_setting( &self, ctx: &Session, params: UpdateUserNotifySettingParams, ) -> Result { let user_uid = ctx.user().ok_or(AppError::Unauthorized)?; let current = self.ensure_user_notify_setting(user_uid).await?; let digest_frequency = parse_enum( params.digest_frequency, current.digest_frequency, DigestFrequency::Unknown, "digest_frequency", )?; sqlx::query_as::<_, UserNotifySetting>( "UPDATE user_notify_setting SET email_notifications = $1, web_notifications = $2, \ mention_notifications = $3, review_notifications = $4, security_notifications = $5, \ marketing_emails = $6, digest_frequency = $7, updated_at = $8 WHERE user_id = $9 \ RETURNING user_id, email_notifications, web_notifications, mention_notifications, \ review_notifications, security_notifications, marketing_emails, digest_frequency, \ created_at, updated_at", ) .bind( params .email_notifications .unwrap_or(current.email_notifications), ) .bind( params .web_notifications .unwrap_or(current.web_notifications), ) .bind( params .mention_notifications .unwrap_or(current.mention_notifications), ) .bind( params .review_notifications .unwrap_or(current.review_notifications), ) .bind( params .security_notifications .unwrap_or(current.security_notifications), ) .bind(params.marketing_emails.unwrap_or(current.marketing_emails)) .bind(digest_frequency) .bind(chrono::Utc::now()) .bind(user_uid) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } async fn ensure_user_notify_setting( &self, user_uid: uuid::Uuid, ) -> Result { if let Some(setting) = self.find_user_notify_setting(user_uid).await? { return Ok(setting); } let now = chrono::Utc::now(); sqlx::query( "INSERT INTO user_notify_setting (user_id, email_notifications, web_notifications, \ mention_notifications, review_notifications, security_notifications, marketing_emails, \ digest_frequency, created_at, updated_at) \ VALUES ($1, true, true, true, true, true, false, 'realtime', $2, $2) ON CONFLICT (user_id) DO NOTHING", ) .bind(user_uid) .bind(now) .execute(self.ctx.db.writer()) .await .map_err(AppError::Database)?; self.find_user_notify_setting(user_uid) .await? .ok_or(AppError::UserNotFound) } async fn find_user_notify_setting( &self, user_uid: uuid::Uuid, ) -> Result, AppError> { sqlx::query_as::<_, UserNotifySetting>( "SELECT user_id, email_notifications, web_notifications, mention_notifications, \ review_notifications, security_notifications, marketing_emails, digest_frequency, \ created_at, updated_at FROM user_notify_setting WHERE user_id = $1", ) .bind(user_uid) .fetch_optional(self.ctx.db.reader()) .await .map_err(AppError::Database) } }