diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 41cde3bf42..24b0feb2e9 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1209,6 +1209,7 @@ where id: collaborator.project_id, host_user_id: Default::default(), connection_ids: Default::default(), + host_connection_id: Default::default(), }); let collaborator_connection_id = @@ -1219,6 +1220,8 @@ where if collaborator.is_host { left_project.host_user_id = collaborator.user_id; + left_project.host_connection_id = + ConnectionId(collaborator.connection_id as u32); } } } @@ -1474,7 +1477,8 @@ where .bind(user_id) .bind(connection_id.0 as i32) .fetch_one(&mut tx) - .await?; + .await + .unwrap(); if !worktrees.is_empty() { let mut params = "(?, ?, ?, ?, ?, ?, ?),".repeat(worktrees.len()); @@ -1505,7 +1509,7 @@ where .bind(0) .bind(false); } - query.execute(&mut tx).await?; + query.execute(&mut tx).await.unwrap(); } sqlx::query( @@ -1526,7 +1530,8 @@ where .bind(0) .bind(true) .execute(&mut tx) - .await?; + .await + .unwrap(); let room = self.commit_room_transaction(room_id, tx).await?; Ok((project_id, room)) @@ -2086,6 +2091,64 @@ where .await } + pub async fn leave_project( + &self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + self.transact(|mut tx| async move { + let result = sqlx::query( + " + DELETE FROM project_collaborators + WHERE project_id = $1 AND connection_id = $2 + ", + ) + .bind(project_id) + .bind(connection_id.0 as i32) + .execute(&mut tx) + .await?; + + if result.rows_affected() != 1 { + Err(anyhow!("not a collaborator on this project"))?; + } + + let connection_ids = sqlx::query_scalar::<_, i32>( + " + SELECT connection_id + FROM project_collaborators + WHERE project_id = $1 + ", + ) + .bind(project_id) + .fetch_all(&mut tx) + .await? + .into_iter() + .map(|id| ConnectionId(id as u32)) + .collect(); + + let (host_user_id, host_connection_id) = sqlx::query_as::<_, (i32, i32)>( + " + SELECT host_user_id, host_connection_id + FROM projects + WHERE id = $1 + ", + ) + .bind(project_id) + .fetch_one(&mut tx) + .await?; + + tx.commit().await?; + + Ok(LeftProject { + id: project_id, + host_user_id: UserId(host_user_id), + host_connection_id: ConnectionId(host_connection_id as u32), + connection_ids, + }) + }) + .await + } + pub async fn project_collaborators( &self, project_id: ProjectId, @@ -2645,6 +2708,7 @@ struct LanguageServer { pub struct LeftProject { pub id: ProjectId, pub host_user_id: UserId, + pub host_connection_id: ConnectionId, pub connection_ids: Vec, } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index db8f25fdb2..c32bdb5008 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1041,8 +1041,11 @@ impl Server { let project_id = ProjectId::from_proto(request.payload.project_id); let project; { - let mut store = self.store().await; - project = store.leave_project(project_id, sender_id)?; + project = self + .app_state + .db + .leave_project(project_id, sender_id) + .await?; tracing::info!( %project_id, host_user_id = %project.host_user_id, @@ -1050,17 +1053,15 @@ impl Server { "leave project" ); - if project.remove_collaborator { - broadcast(sender_id, project.connection_ids, |conn_id| { - self.peer.send( - conn_id, - proto::RemoveProjectCollaborator { - project_id: project_id.to_proto(), - peer_id: sender_id.0, - }, - ) - }); - } + broadcast(sender_id, project.connection_ids, |conn_id| { + self.peer.send( + conn_id, + proto::RemoveProjectCollaborator { + project_id: project_id.to_proto(), + peer_id: sender_id.0, + }, + ) + }); } Ok(()) diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 57dd726d3f..9c93f0daca 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -251,37 +251,6 @@ impl Store { } } - pub fn leave_project( - &mut self, - project_id: ProjectId, - connection_id: ConnectionId, - ) -> Result { - let project = self - .projects - .get_mut(&project_id) - .ok_or_else(|| anyhow!("no such project"))?; - - // If the connection leaving the project is a collaborator, remove it. - let remove_collaborator = if let Some(guest) = project.guests.remove(&connection_id) { - project.active_replica_ids.remove(&guest.replica_id); - true - } else { - false - }; - - if let Some(connection) = self.connections.get_mut(&connection_id) { - connection.projects.remove(&project_id); - } - - Ok(LeftProject { - id: project.id, - host_connection_id: project.host_connection_id, - host_user_id: project.host.user_id, - connection_ids: project.connection_ids(), - remove_collaborator, - }) - } - #[cfg(test)] pub fn check_invariants(&self) { for (connection_id, connection) in &self.connections { diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 4dbade4fec..66ba6a4029 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -24,7 +24,7 @@ use std::{ }; use tracing::instrument; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] pub struct ConnectionId(pub u32); impl fmt::Display for ConnectionId {