143 lines
5.3 KiB
Rust
143 lines
5.3 KiB
Rust
use crate::error::RoomError;
|
|
use crate::service::RoomService;
|
|
use crate::ws_context::WsUserContext;
|
|
use models::projects::project_members;
|
|
use models::rooms::room_access;
|
|
use models::rooms::room_user_state;
|
|
use models::users::user as user_model;
|
|
use sea_orm::*;
|
|
use uuid::Uuid;
|
|
|
|
impl RoomService {
|
|
/// List room participants: for public rooms, all project members;
|
|
/// for private rooms, project admins + room_access grantees + creator.
|
|
pub async fn room_participant_list(
|
|
&self,
|
|
room_id: Uuid,
|
|
ctx: &WsUserContext,
|
|
) -> Result<super::RoomParticipantListResponse, RoomError> {
|
|
self.require_room_access(room_id, ctx.user_id).await?;
|
|
let room_model = self.find_room_or_404(room_id).await?;
|
|
|
|
let members: Vec<Uuid> = if room_model.public {
|
|
// Public room: all project members
|
|
project_members::Entity::find()
|
|
.filter(project_members::Column::Project.eq(room_model.project))
|
|
.all(&self.db)
|
|
.await?
|
|
.into_iter()
|
|
.map(|m| m.user)
|
|
.collect()
|
|
} else {
|
|
// Private room: creator + project admins + room_access grantees
|
|
let mut user_ids = vec![room_model.created_by];
|
|
|
|
// Project admins
|
|
let project_admins = project_members::Entity::find()
|
|
.filter(project_members::Column::Project.eq(room_model.project))
|
|
.all(&self.db)
|
|
.await?;
|
|
for m in project_admins {
|
|
if matches!(
|
|
m.scope_role(),
|
|
Ok(models::projects::MemberRole::Owner | models::projects::MemberRole::Admin)
|
|
) {
|
|
if !user_ids.contains(&m.user) {
|
|
user_ids.push(m.user);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Explicit access grants
|
|
let access_list = room_access::Entity::find()
|
|
.filter(room_access::Column::Room.eq(room_id))
|
|
.all(&self.db)
|
|
.await?;
|
|
for a in access_list {
|
|
if !user_ids.contains(&a.user) {
|
|
user_ids.push(a.user);
|
|
}
|
|
}
|
|
|
|
user_ids
|
|
};
|
|
|
|
// Fetch user info
|
|
let users: std::collections::HashMap<Uuid, super::UserInfo> = if !members.is_empty() {
|
|
user_model::Entity::find()
|
|
.filter(user_model::Column::Uid.is_in(members.clone()))
|
|
.all(&self.db)
|
|
.await?
|
|
.into_iter()
|
|
.map(|u| {
|
|
(
|
|
u.uid,
|
|
super::UserInfo {
|
|
uid: u.uid,
|
|
username: u.username,
|
|
avatar_url: u.avatar_url,
|
|
},
|
|
)
|
|
})
|
|
.collect()
|
|
} else {
|
|
std::collections::HashMap::new()
|
|
};
|
|
|
|
// Fetch room_user_state for each member (for last_read_seq, DND etc.)
|
|
let states = room_user_state::Entity::find()
|
|
.filter(room_user_state::Column::Room.eq(room_id))
|
|
.filter(room_user_state::Column::User.is_in(members.clone()))
|
|
.all(&self.db)
|
|
.await?;
|
|
let state_map: std::collections::HashMap<(Uuid, Uuid), room_user_state::Model> =
|
|
states.into_iter().map(|s| ((s.room, s.user), s)).collect();
|
|
|
|
// Determine project-level role for each member
|
|
let project_member_list = project_members::Entity::find()
|
|
.filter(project_members::Column::Project.eq(room_model.project))
|
|
.filter(project_members::Column::User.is_in(members.clone()))
|
|
.all(&self.db)
|
|
.await?;
|
|
let role_map: std::collections::HashMap<Uuid, String> = project_member_list
|
|
.into_iter()
|
|
.map(|m| {
|
|
(
|
|
m.user,
|
|
m.scope_role()
|
|
.map(|r| r.to_string())
|
|
.unwrap_or_else(|_| "member".to_string()),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
let participants = members
|
|
.into_iter()
|
|
.map(|user_id| {
|
|
let user_info = users.get(&user_id).cloned();
|
|
let state = state_map.get(&(room_id, user_id));
|
|
let project_role = role_map
|
|
.get(&user_id)
|
|
.cloned()
|
|
.unwrap_or_else(|| "member".to_string());
|
|
let is_room_owner = room_model.created_by == user_id;
|
|
|
|
super::RoomParticipantResponse {
|
|
room: room_id,
|
|
user: user_id,
|
|
user_info,
|
|
project_role,
|
|
is_room_owner,
|
|
last_read_seq: state.and_then(|s| s.last_read_seq),
|
|
do_not_disturb: state.map(|s| s.do_not_disturb).unwrap_or(false),
|
|
dnd_start_hour: state.and_then(|s| s.dnd_start_hour),
|
|
dnd_end_hour: state.and_then(|s| s.dnd_end_hour),
|
|
joined_at: state.and_then(|s| s.joined_at),
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
Ok(super::RoomParticipantListResponse { participants })
|
|
}
|
|
}
|