Track room participant role

(Also wire that through to project collaboration rules for now)
This commit is contained in:
Conrad Irwin 2024-01-02 21:07:46 -07:00
parent 88ed5f7290
commit bf304b3fe7
6 changed files with 73 additions and 9 deletions

View file

@ -36,6 +36,7 @@ impl ParticipantLocation {
pub struct LocalParticipant { pub struct LocalParticipant {
pub projects: Vec<proto::ParticipantProject>, pub projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>, pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -247,14 +247,18 @@ impl Room {
let response = client.request(proto::CreateRoom {}).await?; let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new_model(|cx| { let room = cx.new_model(|cx| {
Self::new( let mut room = Self::new(
room_proto.id, room_proto.id,
None, None,
response.live_kit_connection_info, response.live_kit_connection_info,
client, client,
user_store, user_store,
cx, cx,
) );
if let Some(participant) = room_proto.participants.first() {
room.local_participant.role = participant.role()
}
room
})?; })?;
let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = if let Some(initial_project) = initial_project {
@ -710,7 +714,21 @@ impl Room {
this.participant_user_ids.clear(); this.participant_user_ids.clear();
if let Some(participant) = local_participant { if let Some(participant) = local_participant {
let role = participant.role();
this.local_participant.projects = participant.projects; this.local_participant.projects = participant.projects;
if this.local_participant.role != role {
this.local_participant.role = role;
// TODO!() this may be better done using optional replica ids instead.
// (though need to figure out how to handle promotion? join and leave the project?)
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade() {
project.update(cx, |project, _| project.set_role(role));
true
} else {
false
}
});
}
} else { } else {
this.local_participant.projects.clear(); this.local_participant.projects.clear();
} }
@ -1091,10 +1109,19 @@ impl Room {
) -> Task<Result<Model<Project>>> { ) -> Task<Result<Model<Project>>> {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let role = self.local_participant.role;
cx.emit(Event::RemoteProjectJoined { project_id: id }); cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move { cx.spawn(move |this, mut cx| async move {
let project = let project = Project::remote(
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?; id,
client,
user_store,
language_registry,
fs,
role,
cx.clone(),
)
.await?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| { this.joined_projects.retain(|project| {

View file

@ -165,15 +165,16 @@ impl Database {
if role.is_none() || role == Some(ChannelRole::Banned) { if role.is_none() || role == Some(ChannelRole::Banned) {
Err(anyhow!("not allowed"))? Err(anyhow!("not allowed"))?
} }
let role = role.unwrap();
let live_kit_room = format!("channel-{}", nanoid::nanoid!(30)); let live_kit_room = format!("channel-{}", nanoid::nanoid!(30));
let room_id = self let room_id = self
.get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx) .get_or_create_channel_room(channel_id, &live_kit_room, environment, &*tx)
.await?; .await?;
self.join_channel_room_internal(room_id, user_id, connection, &*tx) self.join_channel_room_internal(room_id, user_id, connection, role, &*tx)
.await .await
.map(|jr| (jr, accept_invite_result, role.unwrap())) .map(|jr| (jr, accept_invite_result, role))
}) })
.await .await
} }

View file

@ -131,7 +131,12 @@ impl Database {
connection.owner_id as i32, connection.owner_id as i32,
))), ))),
participant_index: ActiveValue::set(Some(0)), participant_index: ActiveValue::set(Some(0)),
..Default::default() role: ActiveValue::set(Some(ChannelRole::Admin)),
id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
initial_project_id: ActiveValue::NotSet,
} }
.insert(&*tx) .insert(&*tx)
.await?; .await?;
@ -162,7 +167,13 @@ impl Database {
calling_connection.owner_id as i32, calling_connection.owner_id as i32,
))), ))),
initial_project_id: ActiveValue::set(initial_project_id), initial_project_id: ActiveValue::set(initial_project_id),
..Default::default() role: ActiveValue::set(Some(ChannelRole::Member)),
id: ActiveValue::NotSet,
answering_connection_id: ActiveValue::NotSet,
answering_connection_server_id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
} }
.insert(&*tx) .insert(&*tx)
.await?; .await?;
@ -384,6 +395,7 @@ impl Database {
room_id: RoomId, room_id: RoomId,
user_id: UserId, user_id: UserId,
connection: ConnectionId, connection: ConnectionId,
role: ChannelRole,
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
) -> Result<JoinRoom> { ) -> Result<JoinRoom> {
let participant_index = self let participant_index = self
@ -404,7 +416,11 @@ impl Database {
connection.owner_id as i32, connection.owner_id as i32,
))), ))),
participant_index: ActiveValue::Set(Some(participant_index)), participant_index: ActiveValue::Set(Some(participant_index)),
..Default::default() role: ActiveValue::set(Some(role)),
id: ActiveValue::NotSet,
location_kind: ActiveValue::NotSet,
location_project_id: ActiveValue::NotSet,
initial_project_id: ActiveValue::NotSet,
}]) }])
.on_conflict( .on_conflict(
OnConflict::columns([room_participant::Column::UserId]) OnConflict::columns([room_participant::Column::UserId])
@ -413,6 +429,7 @@ impl Database {
room_participant::Column::AnsweringConnectionServerId, room_participant::Column::AnsweringConnectionServerId,
room_participant::Column::AnsweringConnectionLost, room_participant::Column::AnsweringConnectionLost,
room_participant::Column::ParticipantIndex, room_participant::Column::ParticipantIndex,
room_participant::Column::Role,
]) ])
.to_owned(), .to_owned(),
) )

View file

@ -19,6 +19,7 @@ use project::{
search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath, search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath,
}; };
use rand::prelude::*; use rand::prelude::*;
use rpc::proto::ChannelRole;
use serde_json::json; use serde_json::json;
use settings::SettingsStore; use settings::SettingsStore;
use std::{ use std::{
@ -3550,6 +3551,7 @@ async fn test_leaving_project(
client_b.user_store().clone(), client_b.user_store().clone(),
client_b.language_registry().clone(), client_b.language_registry().clone(),
FakeFs::new(cx.background_executor().clone()), FakeFs::new(cx.background_executor().clone()),
ChannelRole::Member,
cx, cx,
) )
}) })

View file

@ -262,6 +262,8 @@ enum ProjectClientState {
}, },
Remote { Remote {
sharing_has_stopped: bool, sharing_has_stopped: bool,
// todo!() this should be represented differently!
is_read_only: bool,
remote_id: u64, remote_id: u64,
replica_id: ReplicaId, replica_id: ReplicaId,
}, },
@ -702,6 +704,7 @@ impl Project {
user_store: Model<UserStore>, user_store: Model<UserStore>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
role: proto::ChannelRole,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<Model<Self>> { ) -> Result<Model<Self>> {
client.authenticate_and_connect(true, &cx).await?; client.authenticate_and_connect(true, &cx).await?;
@ -757,6 +760,7 @@ impl Project {
client: client.clone(), client: client.clone(),
client_state: Some(ProjectClientState::Remote { client_state: Some(ProjectClientState::Remote {
sharing_has_stopped: false, sharing_has_stopped: false,
is_read_only: false,
remote_id, remote_id,
replica_id, replica_id,
}), }),
@ -797,6 +801,7 @@ impl Project {
prettiers_per_worktree: HashMap::default(), prettiers_per_worktree: HashMap::default(),
prettier_instances: HashMap::default(), prettier_instances: HashMap::default(),
}; };
this.set_role(role);
for worktree in worktrees { for worktree in worktrees {
let _ = this.add_worktree(&worktree, cx); let _ = this.add_worktree(&worktree, cx);
} }
@ -1619,6 +1624,13 @@ impl Project {
cx.notify(); cx.notify();
} }
pub fn set_role(&mut self, role: proto::ChannelRole) {
if let Some(ProjectClientState::Remote { is_read_only, .. }) = &mut self.client_state {
*is_read_only =
!(role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin)
}
}
fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) { fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
if let Some(ProjectClientState::Remote { if let Some(ProjectClientState::Remote {
sharing_has_stopped, sharing_has_stopped,
@ -1672,6 +1684,10 @@ impl Project {
pub fn is_read_only(&self) -> bool { pub fn is_read_only(&self) -> bool {
self.is_disconnected() self.is_disconnected()
|| match &self.client_state {
Some(ProjectClientState::Remote { is_read_only, .. }) => *is_read_only,
_ => false,
}
} }
pub fn is_local(&self) -> bool { pub fn is_local(&self) -> bool {