Detailed changes
@@ -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)]
@@ -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| {
@@ -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
}
@@ -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(),
)
@@ -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,
)
})
@@ -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 {