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 projects: Vec<proto::ParticipantProject>,
pub active_project: Option<WeakModel<Project>>,
pub role: proto::ChannelRole,
}
#[derive(Clone, Debug)]

View File

@ -247,14 +247,18 @@ impl Room {
let response = client.request(proto::CreateRoom {}).await?;
let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?;
let room = cx.new_model(|cx| {
Self::new(
let mut room = Self::new(
room_proto.id,
None,
response.live_kit_connection_info,
client,
user_store,
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 {
@ -710,7 +714,21 @@ impl Room {
this.participant_user_ids.clear();
if let Some(participant) = local_participant {
let role = participant.role();
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 {
this.local_participant.projects.clear();
}
@ -1091,10 +1109,19 @@ impl Room {
) -> Task<Result<Model<Project>>> {
let client = self.client.clone();
let user_store = self.user_store.clone();
let role = self.local_participant.role;
cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move {
let project =
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
let project = Project::remote(
id,
client,
user_store,
language_registry,
fs,
role,
cx.clone(),
)
.await?;
this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {

View File

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

View File

@ -131,7 +131,12 @@ impl Database {
connection.owner_id as i32,
))),
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)
.await?;
@ -162,7 +167,13 @@ impl Database {
calling_connection.owner_id as i32,
))),
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)
.await?;
@ -384,6 +395,7 @@ impl Database {
room_id: RoomId,
user_id: UserId,
connection: ConnectionId,
role: ChannelRole,
tx: &DatabaseTransaction,
) -> Result<JoinRoom> {
let participant_index = self
@ -404,7 +416,11 @@ impl Database {
connection.owner_id as i32,
))),
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(
OnConflict::columns([room_participant::Column::UserId])
@ -413,6 +429,7 @@ impl Database {
room_participant::Column::AnsweringConnectionServerId,
room_participant::Column::AnsweringConnectionLost,
room_participant::Column::ParticipantIndex,
room_participant::Column::Role,
])
.to_owned(),
)

View File

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

View File

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