WIP - Retain hosts' project state when they disconnect

Max Brunsfeld created

Change summary

crates/call/src/room.rs | 18 ++++++++++++--
crates/collab/src/db.rs | 51 ++++++++++++++++++++----------------------
2 files changed, 39 insertions(+), 30 deletions(-)

Detailed changes

crates/call/src/room.rs 🔗

@@ -43,6 +43,7 @@ pub struct Room {
     id: u64,
     live_kit: Option<LiveKitRoom>,
     status: RoomStatus,
+    shared_projects: HashSet<WeakModelHandle<Project>>,
     local_participant: LocalParticipant,
     remote_participants: BTreeMap<u64, RemoteParticipant>,
     pending_participants: Vec<Arc<User>>,
@@ -132,6 +133,7 @@ impl Room {
             id,
             live_kit: live_kit_room,
             status: RoomStatus::Online,
+            shared_projects: Default::default(),
             participant_user_ids: Default::default(),
             local_participant: Default::default(),
             remote_participants: Default::default(),
@@ -291,9 +293,18 @@ impl Room {
                                             .ok_or_else(|| anyhow!("room was dropped"))?
                                             .update(&mut cx, |this, cx| {
                                                 this.status = RoomStatus::Online;
-                                                this.apply_room_update(room_proto, cx)
-                                            })?;
-                                        anyhow::Ok(())
+                                                this.apply_room_update(room_proto, cx)?;
+                                                this.shared_projects.retain(|project| {
+                                                    let Some(project) = project.upgrade(cx) else { return false };
+                                                    project.update(cx, |project, cx| {
+                                                        if let Some(remote_id) = project.remote_id() {
+                                                            project.shared(remote_id, cx).detach()
+                                                        }
+                                                    });
+                                                    true
+                                                });
+                                                anyhow::Ok(())
+                                            })
                                     };
 
                                     if rejoin_room.await.log_err().is_some() {
@@ -666,6 +677,7 @@ impl Room {
 
             // If the user's location is in this project, it changes from UnsharedProject to SharedProject.
             this.update(&mut cx, |this, cx| {
+                this.shared_projects.insert(project.downgrade());
                 let active_project = this.local_participant.active_project.as_ref();
                 if active_project.map_or(false, |location| *location == project) {
                     this.set_location(Some(&project), cx)

crates/collab/src/db.rs 🔗

@@ -1601,10 +1601,11 @@ impl Database {
             .exec(&*tx)
             .await?;
 
-            let collaborator_on_projects = project_collaborator::Entity::find()
+            let guest_collaborators_and_projects = project_collaborator::Entity::find()
                 .find_also_related(project::Entity)
                 .filter(
                     Condition::all()
+                        .add(project_collaborator::Column::IsHost.eq(false))
                         .add(project_collaborator::Column::ConnectionId.eq(connection.id as i32))
                         .add(
                             project_collaborator::Column::ConnectionServerId
@@ -1613,40 +1614,36 @@ impl Database {
                 )
                 .all(&*tx)
                 .await?;
+
             project_collaborator::Entity::delete_many()
                 .filter(
-                    Condition::all()
-                        .add(project_collaborator::Column::ConnectionId.eq(connection.id as i32))
-                        .add(
-                            project_collaborator::Column::ConnectionServerId
-                                .eq(connection.owner_id as i32),
-                        ),
+                    project_collaborator::Column::Id
+                        .is_in(guest_collaborators_and_projects.iter().map(|e| e.0.id)),
                 )
                 .exec(&*tx)
                 .await?;
 
             let mut left_projects = Vec::new();
-            for (_, project) in collaborator_on_projects {
-                if let Some(project) = project {
-                    let collaborators = project
-                        .find_related(project_collaborator::Entity)
-                        .all(&*tx)
-                        .await?;
-                    let connection_ids = collaborators
-                        .into_iter()
-                        .map(|collaborator| ConnectionId {
-                            id: collaborator.connection_id as u32,
-                            owner_id: collaborator.connection_server_id.0 as u32,
-                        })
-                        .collect();
+            for (_, project) in guest_collaborators_and_projects {
+                let Some(project) = project else { continue };
+                let collaborators = project
+                    .find_related(project_collaborator::Entity)
+                    .all(&*tx)
+                    .await?;
+                let connection_ids = collaborators
+                    .into_iter()
+                    .map(|collaborator| ConnectionId {
+                        id: collaborator.connection_id as u32,
+                        owner_id: collaborator.connection_server_id.0 as u32,
+                    })
+                    .collect();
 
-                    left_projects.push(LeftProject {
-                        id: project.id,
-                        host_user_id: project.host_user_id,
-                        host_connection_id: project.host_connection()?,
-                        connection_ids,
-                    });
-                }
+                left_projects.push(LeftProject {
+                    id: project.id,
+                    host_user_id: project.host_user_id,
+                    host_connection_id: project.host_connection()?,
+                    connection_ids,
+                });
             }
 
             project::Entity::delete_many()