ssh remoting: Show the host's GitHub name in the titlebar when sharing an SSH project (#19844)

Bennet Bo Fenner and Thorsten created

The name (GitHub name) of the host was not displayed when sharing an ssh
project.

Previously we assumed that the a collaborator is a host if the
`replica_id` of the collaborator was `0`,
but for ssh project the `replica_id` is actually `1`.

<img width="329" alt="Screenshot 2024-10-28 at 18 16 30"
src="https://github.com/user-attachments/assets/c0151e12-a96f-4f38-aec1-4ed5475a9eaf">


Co-Authored-by: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>

Change summary

crates/client/src/user.rs                  | 2 ++
crates/collab/src/db.rs                    | 1 +
crates/collab/src/db/queries/buffers.rs    | 4 ++++
crates/collab/src/db/tests/buffer_tests.rs | 2 ++
crates/collab/src/rpc.rs                   | 1 +
crates/project/src/project.rs              | 4 ++--
crates/proto/proto/zed.proto               | 1 +
crates/workspace/src/workspace.rs          | 2 +-
8 files changed, 14 insertions(+), 3 deletions(-)

Detailed changes

crates/client/src/user.rs 🔗

@@ -48,6 +48,7 @@ pub struct Collaborator {
     pub peer_id: proto::PeerId,
     pub replica_id: ReplicaId,
     pub user_id: UserId,
+    pub is_host: bool,
 }
 
 impl PartialOrd for User {
@@ -824,6 +825,7 @@ impl Collaborator {
             peer_id: message.peer_id.ok_or_else(|| anyhow!("invalid peer id"))?,
             replica_id: message.replica_id as ReplicaId,
             user_id: message.user_id as UserId,
+            is_host: message.is_host,
         })
     }
 }

crates/collab/src/db.rs 🔗

@@ -740,6 +740,7 @@ impl ProjectCollaborator {
             peer_id: Some(self.connection_id.into()),
             replica_id: self.replica_id.0 as u32,
             user_id: self.user_id.to_proto(),
+            is_host: self.is_host,
         }
     }
 }

crates/collab/src/db/queries/buffers.rs 🔗

@@ -116,6 +116,7 @@ impl Database {
                         peer_id: Some(collaborator.connection().into()),
                         user_id: collaborator.user_id.to_proto(),
                         replica_id: collaborator.replica_id.0 as u32,
+                        is_host: false,
                     })
                     .collect(),
             })
@@ -222,6 +223,7 @@ impl Database {
                                 peer_id: Some(collaborator.connection().into()),
                                 user_id: collaborator.user_id.to_proto(),
                                 replica_id: collaborator.replica_id.0 as u32,
+                                is_host: false,
                             })
                             .collect(),
                     },
@@ -257,6 +259,7 @@ impl Database {
                         peer_id: Some(db_collaborator.connection().into()),
                         replica_id: db_collaborator.replica_id.0 as u32,
                         user_id: db_collaborator.user_id.to_proto(),
+                        is_host: false,
                     })
                 } else {
                     collaborator_ids_to_remove.push(db_collaborator.id);
@@ -385,6 +388,7 @@ impl Database {
                 peer_id: Some(connection.into()),
                 replica_id: row.replica_id.0 as u32,
                 user_id: row.user_id.to_proto(),
+                is_host: false,
             });
         }
 

crates/collab/src/db/tests/buffer_tests.rs 🔗

@@ -121,11 +121,13 @@ async fn test_channel_buffers(db: &Arc<Database>) {
                 user_id: a_id.to_proto(),
                 peer_id: Some(rpc::proto::PeerId { id: 1, owner_id }),
                 replica_id: 0,
+                is_host: false,
             },
             rpc::proto::Collaborator {
                 user_id: b_id.to_proto(),
                 peer_id: Some(rpc::proto::PeerId { id: 2, owner_id }),
                 replica_id: 1,
+                is_host: false,
             }
         ]
     );

crates/collab/src/rpc.rs 🔗

@@ -1827,6 +1827,7 @@ fn join_project_internal(
             peer_id: Some(session.connection_id.into()),
             replica_id: replica_id.0 as u32,
             user_id: guest_user_id.to_proto(),
+            is_host: false,
         }),
     };
 

crates/project/src/project.rs 🔗

@@ -1333,7 +1333,7 @@ impl Project {
     }
 
     pub fn host(&self) -> Option<&Collaborator> {
-        self.collaborators.values().find(|c| c.replica_id == 0)
+        self.collaborators.values().find(|c| c.is_host)
     }
 
     pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut AppContext) {
@@ -3495,7 +3495,7 @@ impl Project {
                 .collaborators
                 .remove(&old_peer_id)
                 .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
-            let is_host = collaborator.replica_id == 0;
+            let is_host = collaborator.is_host;
             this.collaborators.insert(new_peer_id, collaborator);
 
             log::info!("peer {} became {}", old_peer_id, new_peer_id,);

crates/proto/proto/zed.proto 🔗

@@ -1721,6 +1721,7 @@ message Collaborator {
     PeerId peer_id = 1;
     uint32 replica_id = 2;
     uint64 user_id = 3;
+    bool is_host = 4;
 }
 
 message User {

crates/workspace/src/workspace.rs 🔗

@@ -5715,7 +5715,7 @@ pub fn join_in_room_project(
                             .read(cx)
                             .collaborators()
                             .values()
-                            .find(|collaborator| collaborator.replica_id == 0)?;
+                            .find(|collaborator| collaborator.is_host)?;
                         Some(collaborator.peer_id)
                     });