Improve local collaboration script to accept a zed impersonate

Mikayla and max created

Gate channels UI behind a flag

co-authored-by: max <max@zed.dev>

Change summary

Cargo.lock                           |   1 
crates/collab_ui/Cargo.toml          |   2 
crates/collab_ui/src/collab_panel.rs | 149 +++++++++++++++--------------
script/start-local-collaboration     |   2 
4 files changed, 80 insertions(+), 74 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1568,6 +1568,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "settings",
+ "staff_mode",
  "theme",
  "theme_selector",
  "util",

crates/collab_ui/Cargo.toml 🔗

@@ -38,6 +38,7 @@ picker = { path = "../picker" }
 project = { path = "../project" }
 recent_projects = {path = "../recent_projects"}
 settings = { path = "../settings" }
+staff_mode = {path = "../staff_mode"}
 theme = { path = "../theme" }
 theme_selector = { path = "../theme_selector" }
 vcs_menu = { path = "../vcs_menu" }
@@ -45,7 +46,6 @@ util = { path = "../util" }
 workspace = { path = "../workspace" }
 zed-actions = {path = "../zed-actions"}
 
-
 anyhow.workspace = true
 futures.workspace = true
 log.workspace = true

crates/collab_ui/src/collab_panel.rs 🔗

@@ -31,6 +31,7 @@ use panel_settings::{CollaborationPanelDockPosition, CollaborationPanelSettings}
 use project::{Fs, Project};
 use serde_derive::{Deserialize, Serialize};
 use settings::SettingsStore;
+use staff_mode::StaffMode;
 use std::{mem, sync::Arc};
 use theme::IconButton;
 use util::{ResultExt, TryFutureExt};
@@ -347,6 +348,8 @@ impl CollabPanel {
                 .push(cx.observe(&this.channel_store, |this, _, cx| this.update_entries(cx)));
             this.subscriptions
                 .push(cx.observe(&active_call, |this, _, cx| this.update_entries(cx)));
+            this.subscriptions
+                .push(cx.observe_global::<StaffMode, _>(move |this, cx| this.update_entries(cx)));
 
             this
         })
@@ -516,79 +519,76 @@ impl CollabPanel {
             }
         }
 
-        self.entries.push(ListEntry::Header(Section::Channels, 0));
+        let mut request_entries = Vec::new();
+        if self.include_channels_section(cx) {
+            self.entries.push(ListEntry::Header(Section::Channels, 0));
 
-        let channels = channel_store.channels();
-        if !(channels.is_empty() && self.channel_editing_state.is_none()) {
-            self.match_candidates.clear();
-            self.match_candidates
-                .extend(
-                    channels
-                        .iter()
-                        .enumerate()
-                        .map(|(ix, channel)| StringMatchCandidate {
+            let channels = channel_store.channels();
+            if !(channels.is_empty() && self.channel_editing_state.is_none()) {
+                self.match_candidates.clear();
+                self.match_candidates
+                    .extend(channels.iter().enumerate().map(|(ix, channel)| {
+                        StringMatchCandidate {
                             id: ix,
                             string: channel.name.clone(),
                             char_bag: channel.name.chars().collect(),
-                        }),
-                );
-            let matches = executor.block(match_strings(
-                &self.match_candidates,
-                &query,
-                true,
-                usize::MAX,
-                &Default::default(),
-                executor.clone(),
-            ));
-            if let Some(state) = &self.channel_editing_state {
-                if state.parent_id.is_none() {
-                    self.entries.push(ListEntry::ChannelEditor { depth: 0 });
-                }
-            }
-            for mat in matches {
-                let channel = &channels[mat.candidate_id];
-                self.entries.push(ListEntry::Channel(channel.clone()));
+                        }
+                    }));
+                let matches = executor.block(match_strings(
+                    &self.match_candidates,
+                    &query,
+                    true,
+                    usize::MAX,
+                    &Default::default(),
+                    executor.clone(),
+                ));
                 if let Some(state) = &self.channel_editing_state {
-                    if state.parent_id == Some(channel.id) {
-                        self.entries.push(ListEntry::ChannelEditor {
-                            depth: channel.depth + 1,
-                        });
+                    if state.parent_id.is_none() {
+                        self.entries.push(ListEntry::ChannelEditor { depth: 0 });
+                    }
+                }
+                for mat in matches {
+                    let channel = &channels[mat.candidate_id];
+                    self.entries.push(ListEntry::Channel(channel.clone()));
+                    if let Some(state) = &self.channel_editing_state {
+                        if state.parent_id == Some(channel.id) {
+                            self.entries.push(ListEntry::ChannelEditor {
+                                depth: channel.depth + 1,
+                            });
+                        }
                     }
                 }
             }
-        }
 
-        let mut request_entries = Vec::new();
-        let channel_invites = channel_store.channel_invitations();
-        if !channel_invites.is_empty() {
-            self.match_candidates.clear();
-            self.match_candidates
-                .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
-                    StringMatchCandidate {
-                        id: ix,
-                        string: channel.name.clone(),
-                        char_bag: channel.name.chars().collect(),
-                    }
+            let channel_invites = channel_store.channel_invitations();
+            if !channel_invites.is_empty() {
+                self.match_candidates.clear();
+                self.match_candidates
+                    .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
+                        StringMatchCandidate {
+                            id: ix,
+                            string: channel.name.clone(),
+                            char_bag: channel.name.chars().collect(),
+                        }
+                    }));
+                let matches = executor.block(match_strings(
+                    &self.match_candidates,
+                    &query,
+                    true,
+                    usize::MAX,
+                    &Default::default(),
+                    executor.clone(),
+                ));
+                request_entries.extend(matches.iter().map(|mat| {
+                    ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
                 }));
-            let matches = executor.block(match_strings(
-                &self.match_candidates,
-                &query,
-                true,
-                usize::MAX,
-                &Default::default(),
-                executor.clone(),
-            ));
-            request_entries.extend(
-                matches
-                    .iter()
-                    .map(|mat| ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())),
-            );
 
-            if !request_entries.is_empty() {
-                self.entries
-                    .push(ListEntry::Header(Section::ChannelInvites, 1));
-                if !self.collapsed_sections.contains(&Section::ChannelInvites) {
-                    self.entries.append(&mut request_entries);
+                if !request_entries.is_empty() {
+                    self.entries
+                        .push(ListEntry::Header(Section::ChannelInvites, 1));
+                    if !self.collapsed_sections.contains(&Section::ChannelInvites) {
+                        self.entries.append(&mut request_entries);
+                    }
                 }
             }
         }
@@ -686,16 +686,9 @@ impl CollabPanel {
                 executor.clone(),
             ));
 
-            let (mut online_contacts, offline_contacts) = matches
+            let (online_contacts, offline_contacts) = matches
                 .iter()
                 .partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
-            if let Some(room) = ActiveCall::global(cx).read(cx).room() {
-                let room = room.read(cx);
-                online_contacts.retain(|contact| {
-                    let contact = &contacts[contact.candidate_id];
-                    !room.contains_participant(contact.user.id)
-                });
-            }
 
             for (matches, section) in [
                 (online_contacts, Section::Online),
@@ -1534,6 +1527,14 @@ impl CollabPanel {
             .into_any()
     }
 
+    fn include_channels_section(&self, cx: &AppContext) -> bool {
+        if cx.has_global::<StaffMode>() {
+            cx.global::<StaffMode>().0
+        } else {
+            false
+        }
+    }
+
     fn deploy_channel_context_menu(
         &mut self,
         position: Vector2F,
@@ -1878,8 +1879,12 @@ impl View for CollabPanel {
                     })
                     .on_click(MouseButton::Left, |_, this, cx| {
                         let client = this.client.clone();
-                        cx.spawn(|_, cx| async move {
-                            client.authenticate_and_connect(true, &cx).await.log_err()
+                        cx.spawn(|this, mut cx| async move {
+                            client.authenticate_and_connect(true, &cx).await.log_err();
+
+                            this.update(&mut cx, |_, cx| {
+                                cx.notify();
+                            })
                         })
                         .detach();
                     })

script/start-local-collaboration 🔗

@@ -53,6 +53,6 @@ sleep 0.5
 
 # Start the two Zed child processes. Open the given paths with the first instance.
 trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
-ZED_IMPERSONATE=${username_1} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ &
+ZED_IMPERSONATE=${ZED_IMPERSONATE:=${username_1}} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ &
 SECOND=true ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed &
 wait