951a522
use models::users::user::Audience;
use models::users::user_preferences;
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel, Set};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use super::AuthService;
use crate::errors::AuthError;
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct UserPreferencesDto {
pub theme_mode: String,
pub diff_view_style: Option<String>,
/// Default view for the 问题集 page: `board` or `list`.
pub board_view_mode: String,
pub in_app_flags: i32,
pub email_flags: i32,
pub push_flags: i32,
pub email_digest: bool,
pub mute_until: Option<OffsetDateTime>,
pub allow_dms_from: Audience,
pub allow_mentions_from: Audience,
pub show_online_status: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, utoipa::ToSchema)]
pub struct UpdatePreferencesCmd {
pub theme_mode: Option<String>,
pub diff_view_style: Option<String>,
pub board_view_mode: Option<String>,
pub in_app_flags: Option<i32>,
pub email_flags: Option<i32>,
pub push_flags: Option<i32>,
pub email_digest: Option<bool>,
/// 三态:缺省不动、`null` 清除、给值设置(与 `lib/webapi/src/openapi.rs`
/// 声明的 PATCH 语义一致)。
#[serde(
default,
deserialize_with = "export::serde_datetime::deserialize_opt_opt_datetime"
)]
pub mute_until: Option<Option<OffsetDateTime>>,
pub allow_dms_from: Option<Audience>,
pub allow_mentions_from: Option<Audience>,
pub show_online_status: Option<bool>,
}
impl UserPreferencesDto {
fn defaults() -> Self {
Self {
theme_mode: "system".to_string(),
diff_view_style: None,
board_view_mode: "board".to_string(),
in_app_flags: 0,
email_flags: 0,
push_flags: 0,
email_digest: false,
mute_until: None,
allow_dms_from: Audience::Everyone,
allow_mentions_from: Audience::Everyone,
// 在线状态默认对同空间/同私聊可见(与 Discord/Slack 一致),
// 用户可在隐私设置里显式关闭;关闭后读路径一律表现为离线。
show_online_status: true,
}
}
}
impl AuthService {
pub async fn preferences(&self, uid: i64) -> Result<UserPreferencesDto, AuthError> {
let Some(row) = user_preferences::Entity::find_by_id(uid)
.one(self.db().read())
.await?
else {
return Ok(UserPreferencesDto::defaults());
};
Ok(UserPreferencesDto {
theme_mode: row.theme_mode,
diff_view_style: row.diff_view_style,
board_view_mode: row.board_view_mode,
in_app_flags: row.in_app_flags,
email_flags: row.email_flags,
push_flags: row.push_flags,
email_digest: row.email_digest,
mute_until: row.mute_until,
allow_dms_from: row.allow_dms_from,
allow_mentions_from: row.allow_mentions_from,
show_online_status: row.show_online_status,
})
}
pub async fn update_preferences(
&self,
uid: i64,
cmd: UpdatePreferencesCmd,
) -> Result<UserPreferencesDto, AuthError> {
let db = self.db();
let now = OffsetDateTime::now_utc();
let existing = user_preferences::Entity::find_by_id(uid)
.one(db.write())
.await?;
if let Some(row) = existing {
let mut active = row.into_active_model();
if let Some(theme_mode) = cmd.theme_mode {
active.theme_mode = Set(theme_mode);
}
if cmd.diff_view_style.is_some() {
active.diff_view_style = Set(cmd.diff_view_style);
}
if let Some(view) = cmd.board_view_mode {
active.board_view_mode = Set(view);
}
if let Some(flags) = cmd.in_app_flags {
active.in_app_flags = Set(flags);
}
if let Some(flags) = cmd.email_flags {
active.email_flags = Set(flags);
}
if let Some(flags) = cmd.push_flags {
active.push_flags = Set(flags);
}
if let Some(digest) = cmd.email_digest {
active.email_digest = Set(digest);
}
if let Some(mute_until) = cmd.mute_until {
active.mute_until = Set(mute_until);
}
if let Some(audience) = cmd.allow_dms_from {
active.allow_dms_from = Set(audience);
}
if let Some(audience) = cmd.allow_mentions_from {
active.allow_mentions_from = Set(audience);
}
if let Some(online) = cmd.show_online_status {
active.show_online_status = Set(online);
}
active.updated_at = Set(now);
active.update(db.write()).await?;
} else {
let defaults = UserPreferencesDto::defaults();
let row = user_preferences::ActiveModel {
user_id: Set(uid),
theme_mode: Set(cmd.theme_mode.unwrap_or(defaults.theme_mode)),
theme_token: Set(None),
theme_version: Set(None),
diff_view_style: Set(cmd.diff_view_style.or(defaults.diff_view_style)),
board_view_mode: Set(cmd.board_view_mode.unwrap_or(defaults.board_view_mode)),
in_app_flags: Set(cmd.in_app_flags.unwrap_or(defaults.in_app_flags)),
email_flags: Set(cmd.email_flags.unwrap_or(defaults.email_flags)),
push_flags: Set(cmd.push_flags.unwrap_or(defaults.push_flags)),
email_digest: Set(cmd.email_digest.unwrap_or(defaults.email_digest)),
mute_until: Set(cmd.mute_until.flatten().or(defaults.mute_until)),
allow_dms_from: Set(cmd.allow_dms_from.unwrap_or(defaults.allow_dms_from)),
allow_mentions_from: Set(cmd
.allow_mentions_from
.unwrap_or(defaults.allow_mentions_from)),
show_online_status: Set(cmd
.show_online_status
.unwrap_or(defaults.show_online_status)),
created_at: Set(now),
updated_at: Set(now),
};
row.insert(db.write()).await?;
}
self.preferences(uid).await
}
}