Do more on channel join (#7268)

Conrad Irwin created

This change makes it so that if you are the first to join a channel,
your project is automatically shared.

It also makes it so that if you join a channel via a link and there are
no shared projects, you open the notes instead of an empty workspace
with nothing.

This is to try and address the discoverability of project sharing: we've
had
two reviews that have talked about channels, but not talked about
sharing
projects into them, which makes me suspect they didn't know about the
feature.

Release Notes:

- Added a setting `share_on_join` (defaulting to true). When set, and
you join an empty channel, your project is automatically shared.

Change summary

assets/settings/default.json         |  6 +++-
crates/call/src/call_settings.rs     |  6 ++++
crates/call/src/room.rs              |  6 +++
crates/collab_ui/src/collab_panel.rs | 15 ++++++++++
crates/workspace/src/workspace.rs    | 40 +++++++++++++++++++++++++++++
5 files changed, 68 insertions(+), 5 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -104,8 +104,10 @@
   "show_whitespaces": "selection",
   // Settings related to calls in Zed
   "calls": {
-    // Join calls with the microphone muted by default
-    "mute_on_join": false
+    // Join calls with the microphone live by default
+    "mute_on_join": false,
+    // Share your project when you are the first to join a channel
+    "share_on_join": true
   },
   // Scrollbar related settings
   "scrollbar": {

crates/call/src/call_settings.rs 🔗

@@ -7,6 +7,7 @@ use settings::Settings;
 #[derive(Deserialize, Debug)]
 pub struct CallSettings {
     pub mute_on_join: bool,
+    pub share_on_join: bool,
 }
 
 /// Configuration of voice calls in Zed.
@@ -16,6 +17,11 @@ pub struct CallSettingsContent {
     ///
     /// Default: false
     pub mute_on_join: Option<bool>,
+
+    /// Whether your current project should be shared when joining an empty channel.
+    ///
+    /// Default: true
+    pub share_on_join: Option<bool>,
 }
 
 impl Settings for CallSettings {

crates/call/src/room.rs 🔗

@@ -617,6 +617,10 @@ impl Room {
         self.local_participant.role == proto::ChannelRole::Admin
     }
 
+    pub fn local_participant_is_guest(&self) -> bool {
+        self.local_participant.role == proto::ChannelRole::Guest
+    }
+
     pub fn set_participant_role(
         &mut self,
         user_id: u64,
@@ -1202,7 +1206,7 @@ impl Room {
         })
     }
 
-    pub(crate) fn share_project(
+    pub fn share_project(
         &mut self,
         project: Model<Project>,
         cx: &mut ModelContext<Self>,

crates/collab_ui/src/collab_panel.rs 🔗

@@ -40,7 +40,7 @@ use util::{maybe, ResultExt, TryFutureExt};
 use workspace::{
     dock::{DockPosition, Panel, PanelEvent},
     notifications::{DetachAndPromptErr, NotifyResultExt, NotifyTaskExt},
-    Workspace,
+    OpenChannelNotes, Workspace,
 };
 
 actions!(
@@ -69,6 +69,19 @@ pub fn init(cx: &mut AppContext) {
         workspace.register_action(|workspace, _: &ToggleFocus, cx| {
             workspace.toggle_panel_focus::<CollabPanel>(cx);
         });
+        workspace.register_action(|_, _: &OpenChannelNotes, cx| {
+            let channel_id = ActiveCall::global(cx)
+                .read(cx)
+                .room()
+                .and_then(|room| room.read(cx).channel_id());
+
+            if let Some(channel_id) = channel_id {
+                let workspace = cx.view().clone();
+                cx.window_context().defer(move |cx| {
+                    ChannelView::open(channel_id, workspace, cx).detach_and_log_err(cx)
+                });
+            }
+        });
     })
     .detach();
 }

crates/workspace/src/workspace.rs 🔗

@@ -12,7 +12,7 @@ mod toolbar;
 mod workspace_settings;
 
 use anyhow::{anyhow, Context as _, Result};
-use call::ActiveCall;
+use call::{call_settings::CallSettings, ActiveCall};
 use client::{
     proto::{self, ErrorCode, PeerId},
     Client, ErrorExt, Status, TypedEnvelope, UserStore,
@@ -3974,6 +3974,8 @@ pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
     DB.last_workspace().await.log_err().flatten()
 }
 
+actions!(collab, [OpenChannelNotes]);
+
 async fn join_channel_internal(
     channel_id: u64,
     app_state: &Arc<AppState>,
@@ -4075,6 +4077,36 @@ async fn join_channel_internal(
             return Some(join_remote_project(project, host, app_state.clone(), cx));
         }
 
+        // if you are the first to join a channel, share your project
+        if room.remote_participants().len() == 0 && !room.local_participant_is_guest() {
+            if let Some(workspace) = requesting_window {
+                let project = workspace.update(cx, |workspace, cx| {
+                    if !CallSettings::get_global(cx).share_on_join {
+                        return None;
+                    }
+                    let project = workspace.project.read(cx);
+                    if project.is_local()
+                        && project.visible_worktrees(cx).any(|tree| {
+                            tree.read(cx)
+                                .root_entry()
+                                .map_or(false, |entry| entry.is_dir())
+                        })
+                    {
+                        Some(workspace.project.clone())
+                    } else {
+                        None
+                    }
+                });
+                if let Ok(Some(project)) = project {
+                    return Some(cx.spawn(|room, mut cx| async move {
+                        room.update(&mut cx, |room, cx| room.share_project(project, cx))?
+                            .await?;
+                        Ok(())
+                    }));
+                }
+            }
+        }
+
         None
     })?;
     if let Some(task) = task {
@@ -4117,6 +4149,12 @@ pub fn join_channel(
                 })?
                 .await?;
 
+            if result.is_ok() {
+                cx.update(|cx| {
+                    cx.dispatch_action(&OpenChannelNotes);
+                }).log_err();
+            }
+
             active_window = Some(window_handle);
         }