diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index e242c62f..2b8d3761 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -28,6 +28,9 @@ auto_derived_partial!( /// Member's nickname #[serde(skip_serializing_if = "Option::is_none")] pub nickname: Option, + /// Member's pronouns + #[serde(skip_serializing_if = "Option::is_none")] + pub pronouns: Option, /// Avatar attachment #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, @@ -65,6 +68,7 @@ auto_derived!( /// Optional fields on server member object pub enum FieldsMember { Nickname, + Pronouns, Avatar, Roles, Timeout, @@ -88,6 +92,7 @@ impl Default for Member { id: Default::default(), joined_at: Timestamp::now_utc(), nickname: None, + pronouns: None, avatar: None, roles: vec![], timeout: None, @@ -231,6 +236,7 @@ impl Member { FieldsMember::JoinedAt => {} FieldsMember::Avatar => self.avatar = None, FieldsMember::Nickname => self.nickname = None, + FieldsMember::Pronouns => self.pronouns = None, FieldsMember::Roles => self.roles.clear(), FieldsMember::Timeout => self.timeout = None, FieldsMember::CanReceive => self.can_receive = true, diff --git a/crates/core/database/src/models/server_members/ops/mongodb.rs b/crates/core/database/src/models/server_members/ops/mongodb.rs index 99cce7c9..f9db0d35 100644 --- a/crates/core/database/src/models/server_members/ops/mongodb.rs +++ b/crates/core/database/src/models/server_members/ops/mongodb.rs @@ -345,6 +345,7 @@ impl IntoDocumentPath for FieldsMember { FieldsMember::JoinedAt => Some("joined_at"), FieldsMember::Avatar => Some("avatar"), FieldsMember::Nickname => Some("nickname"), + FieldsMember::Pronouns => Some("pronouns"), FieldsMember::Roles => Some("roles"), FieldsMember::Timeout => Some("timeout"), FieldsMember::CanPublish => Some("can_publish"), diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index 83448519..cb2d3dfe 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -31,6 +31,9 @@ auto_derived_partial!( /// Display name #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, + /// User's pronouns + #[serde(skip_serializing_if = "Option::is_none", default)] + pub pronouns: Option, #[serde(skip_serializing_if = "Option::is_none")] /// Avatar attachment pub avatar: Option, @@ -76,6 +79,7 @@ auto_derived!( ProfileContent, ProfileBackground, DisplayName, + Pronouns, // internal fields Suspension, @@ -182,6 +186,7 @@ impl Default for User { username: Default::default(), discriminator: Default::default(), display_name: Default::default(), + pronouns: Default::default(), avatar: Default::default(), relations: Default::default(), badges: Default::default(), @@ -706,6 +711,7 @@ impl User { } } FieldsUser::DisplayName => self.display_name = None, + FieldsUser::Pronouns => self.pronouns = None, FieldsUser::Suspension => self.suspended_until = None, FieldsUser::None => {} } diff --git a/crates/core/database/src/models/users/ops/mongodb.rs b/crates/core/database/src/models/users/ops/mongodb.rs index 0a6f641f..b2a8eb1d 100644 --- a/crates/core/database/src/models/users/ops/mongodb.rs +++ b/crates/core/database/src/models/users/ops/mongodb.rs @@ -336,6 +336,7 @@ impl IntoDocumentPath for FieldsUser { FieldsUser::StatusPresence => "status.presence", FieldsUser::StatusText => "status.text", FieldsUser::DisplayName => "display_name", + FieldsUser::Pronouns => "pronouns", FieldsUser::Suspension => "suspended_until", FieldsUser::None => "none", }) diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 911bc371..2cce6799 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -631,6 +631,7 @@ impl From for Member { id: value.id.into(), joined_at: value.joined_at, nickname: value.nickname, + pronouns: value.pronouns, avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, @@ -646,6 +647,7 @@ impl From for crate::Member { id: value.id.into(), joined_at: value.joined_at, nickname: value.nickname, + pronouns: value.pronouns, avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, @@ -661,6 +663,7 @@ impl From for PartialMember { id: value.id.map(|id| id.into()), joined_at: value.joined_at, nickname: value.nickname, + pronouns: value.pronouns, avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, @@ -676,6 +679,7 @@ impl From for crate::PartialMember { id: value.id.map(|id| id.into()), joined_at: value.joined_at, nickname: value.nickname, + pronouns: value.pronouns, avatar: value.avatar.map(|f| f.into()), roles: value.roles, timeout: value.timeout, @@ -708,6 +712,7 @@ impl From for FieldsMember { match value { crate::FieldsMember::Avatar => FieldsMember::Avatar, crate::FieldsMember::Nickname => FieldsMember::Nickname, + crate::FieldsMember::Pronouns => FieldsMember::Pronouns, crate::FieldsMember::Roles => FieldsMember::Roles, crate::FieldsMember::Timeout => FieldsMember::Timeout, crate::FieldsMember::CanReceive => FieldsMember::CanReceive, @@ -723,6 +728,7 @@ impl From for crate::FieldsMember { match value { FieldsMember::Avatar => crate::FieldsMember::Avatar, FieldsMember::Nickname => crate::FieldsMember::Nickname, + FieldsMember::Pronouns => crate::FieldsMember::Pronouns, FieldsMember::Roles => crate::FieldsMember::Roles, FieldsMember::Timeout => crate::FieldsMember::Timeout, FieldsMember::CanReceive => crate::FieldsMember::CanReceive, @@ -1032,6 +1038,7 @@ impl crate::User { username: self.username, discriminator: self.discriminator, display_name: self.display_name, + pronouns: self.pronouns, avatar: self.avatar.map(|file| file.into()), relations: if let Some(crate::User { id, .. }) = perspective { if id == &self.id { @@ -1108,6 +1115,7 @@ impl crate::User { username: self.username, discriminator: self.discriminator, display_name: self.display_name, + pronouns: self.pronouns, avatar: self.avatar.map(|file| file.into()), relations: vec![], badges, @@ -1141,6 +1149,7 @@ impl crate::User { username: self.username, discriminator: self.discriminator, display_name: self.display_name, + pronouns: self.pronouns, avatar: self.avatar.map(|file| file.into()), relations: vec![], badges, @@ -1168,6 +1177,7 @@ impl crate::User { username: self.username, discriminator: self.discriminator, display_name: self.display_name, + pronouns: self.pronouns, avatar: self.avatar.map(|file| file.into()), relations: self .relations @@ -1211,6 +1221,7 @@ impl From for crate::User { username: value.username, discriminator: value.discriminator, display_name: value.display_name, + pronouns: value.pronouns, avatar: value.avatar.map(Into::into), relations: None, badges: Some(value.badges as i32), @@ -1231,6 +1242,7 @@ impl From for PartialUser { username: value.username, discriminator: value.discriminator, display_name: value.display_name, + pronouns: value.pronouns, avatar: value.avatar.map(|file| file.into()), relations: value.relations.map(|relationships| { relationships @@ -1259,6 +1271,7 @@ impl From for crate::FieldsUser { FieldsUser::StatusPresence => crate::FieldsUser::StatusPresence, FieldsUser::StatusText => crate::FieldsUser::StatusText, FieldsUser::DisplayName => crate::FieldsUser::DisplayName, + FieldsUser::Pronouns => crate::FieldsUser::Pronouns, FieldsUser::Internal => crate::FieldsUser::None, } @@ -1274,6 +1287,7 @@ impl From for FieldsUser { crate::FieldsUser::StatusPresence => FieldsUser::StatusPresence, crate::FieldsUser::StatusText => FieldsUser::StatusText, crate::FieldsUser::DisplayName => FieldsUser::DisplayName, + crate::FieldsUser::Pronouns => FieldsUser::Pronouns, crate::FieldsUser::Suspension => FieldsUser::Internal, crate::FieldsUser::None => FieldsUser::Internal, diff --git a/crates/core/models/src/v0/server_members.rs b/crates/core/models/src/v0/server_members.rs index 41ec99ae..bcded0b0 100644 --- a/crates/core/models/src/v0/server_members.rs +++ b/crates/core/models/src/v0/server_members.rs @@ -52,6 +52,9 @@ auto_derived_partial!( /// Member's nickname #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub nickname: Option, + /// Member's pronouns + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub pronouns: Option, /// Avatar attachment #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub avatar: Option, @@ -89,6 +92,7 @@ auto_derived!( /// Optional fields on server member object pub enum FieldsMember { Nickname, + Pronouns, Avatar, Roles, Timeout, @@ -136,6 +140,9 @@ auto_derived!( /// Member nickname #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] pub nickname: Option, + /// Member pronouns + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 24)))] + pub pronouns: Option, /// Attachment Id to set for avatar pub avatar: Option, /// Array of role ids diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 73957768..217324c2 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -32,6 +32,9 @@ auto_derived_partial!( /// Display name #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub display_name: Option, + /// User's pronouns + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub pronouns: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] /// Avatar attachment pub avatar: Option, @@ -89,6 +92,7 @@ auto_derived!( ProfileContent, ProfileBackground, DisplayName, + Pronouns, /// Internal field, ignore this. Internal, @@ -225,6 +229,9 @@ auto_derived!( validate(length(min = 2, max = 32), regex = "RE_DISPLAY_NAME") )] pub display_name: Option, + /// New pronouns + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 24)))] + pub pronouns: Option, /// Attachment Id for avatar #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))] pub avatar: Option, diff --git a/crates/delta/src/routes/channels/message_send.rs b/crates/delta/src/routes/channels/message_send.rs index e6551d0c..a809df0c 100644 --- a/crates/delta/src/routes/channels/message_send.rs +++ b/crates/delta/src/routes/channels/message_send.rs @@ -359,6 +359,7 @@ mod test { id: None, joined_at: None, nickname: None, + pronouns: None, avatar: None, timeout: None, roles: Some(second_member_roles), @@ -688,6 +689,7 @@ mod test { id: None, joined_at: None, nickname: None, + pronouns: None, roles: Some(vec![role.id.clone()]), timeout: None, can_publish: None, diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index e3f63e49..848e776f 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -64,6 +64,12 @@ pub async fn edit( } } + if data.pronouns.is_some() || data.remove.contains(&v0::FieldsMember::Pronouns) { + if user.id != member.id.user { + return Err(create_error!(InvalidOperation)) + } + } + if data.avatar.is_some() || data.remove.contains(&v0::FieldsMember::Avatar) { if user.id == member.id.user { permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeAvatar)?; @@ -172,6 +178,7 @@ pub async fn edit( // Apply edits to the member object let v0::DataMemberEdit { nickname, + pronouns, avatar, roles, timeout, @@ -183,6 +190,7 @@ pub async fn edit( let mut partial = PartialMember { nickname, + pronouns, roles, timeout, can_publish, diff --git a/crates/delta/src/routes/users/edit_user.rs b/crates/delta/src/routes/users/edit_user.rs index 535fc05c..1334909f 100644 --- a/crates/delta/src/routes/users/edit_user.rs +++ b/crates/delta/src/routes/users/edit_user.rs @@ -48,6 +48,7 @@ pub async fn edit( // Exit out early if nothing is changed if data.display_name.is_none() + && data.pronouns.is_none() && data.status.is_none() && data.profile.is_none() && data.avatar.is_none() @@ -80,6 +81,7 @@ pub async fn edit( let mut partial: PartialUser = PartialUser { display_name: data.display_name, + pronouns: data.pronouns, badges: data.badges, flags: data.flags, ..Default::default()