Change channel join behavior

Conrad Irwin created

- Clicking on a channel name now joins the channel if you are not in it
- (or opens the notes if you are already there).
- When joining a channel, previously shared projects are opened
  automatically.
- If there are no previously shared projects, the notes are opened.

Change summary

crates/call/src/call.rs              |  6 +-
crates/call/src/room.rs              | 27 +++++++++++++++
crates/collab_ui/src/collab_panel.rs | 51 +++++++++++++++++++++++++----
3 files changed, 74 insertions(+), 10 deletions(-)

Detailed changes

crates/call/src/call.rs 🔗

@@ -291,10 +291,10 @@ impl ActiveCall {
         &mut self,
         channel_id: u64,
         cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
+    ) -> Task<Result<ModelHandle<Room>>> {
         if let Some(room) = self.room().cloned() {
             if room.read(cx).channel_id() == Some(channel_id) {
-                return Task::ready(Ok(()));
+                return Task::ready(Ok(room));
             } else {
                 room.update(cx, |room, cx| room.clear_state(cx));
             }
@@ -309,7 +309,7 @@ impl ActiveCall {
             this.update(&mut cx, |this, cx| {
                 this.report_call_event("join channel", cx)
             });
-            Ok(())
+            Ok(room)
         })
     }
 

crates/call/src/room.rs 🔗

@@ -594,6 +594,33 @@ impl Room {
             .map_or(&[], |v| v.as_slice())
     }
 
+    /// projects_to_join returns a list of shared projects sorted such
+    /// that the most 'active' projects appear last.
+    pub fn projects_to_join(&self) -> Vec<(u64, u64)> {
+        let mut projects = HashMap::default();
+        let mut hosts = HashMap::default();
+        for participant in self.remote_participants.values() {
+            match participant.location {
+                ParticipantLocation::SharedProject { project_id } => {
+                    *projects.entry(project_id).or_insert(0) += 1;
+                }
+                ParticipantLocation::External | ParticipantLocation::UnsharedProject => {}
+            }
+            for project in &participant.projects {
+                *projects.entry(project.id).or_insert(0) += 1;
+                hosts.insert(project.id, participant.user.id);
+            }
+        }
+
+        let mut pairs: Vec<(u64, usize)> = projects.into_iter().collect();
+        pairs.sort_by_key(|(_, count)| 0 - *count as i32);
+
+        pairs
+            .into_iter()
+            .map(|(project_id, _)| (project_id, hosts[&project_id]))
+            .collect()
+    }
+
     async fn handle_room_updated(
         this: ModelHandle<Self>,
         envelope: TypedEnvelope<proto::RoomUpdated>,

crates/collab_ui/src/collab_panel.rs 🔗

@@ -8,7 +8,7 @@ use crate::{
     panel_settings, CollaborationPanelSettings,
 };
 use anyhow::Result;
-use call::ActiveCall;
+use call::{participant, ActiveCall};
 use channel::{Channel, ChannelData, ChannelEvent, ChannelId, ChannelPath, ChannelStore};
 use channel_modal::ChannelModal;
 use client::{proto::PeerId, Client, Contact, User, UserStore};
@@ -1929,7 +1929,7 @@ impl CollabPanel {
                                 }))
                                 .into_any()
                         } else if row_hovered {
-                            Svg::new("icons/speaker-loud.svg")
+                            Svg::new("icons/file.svg")
                                 .with_color(theme.channel_hash.color)
                                 .constrained()
                                 .with_width(theme.channel_hash.width)
@@ -1939,7 +1939,11 @@ impl CollabPanel {
                         }
                     })
                     .on_click(MouseButton::Left, move |_, this, cx| {
-                        this.join_channel_call(channel_id, cx);
+                        if !is_active {
+                            this.join_channel_call(channel_id, cx);
+                        } else {
+                            this.open_channel_notes(&OpenChannelNotes { channel_id }, cx)
+                        }
                     }),
                 )
                 .align_children_center()
@@ -1968,6 +1972,12 @@ impl CollabPanel {
         })
         .on_click(MouseButton::Left, move |_, this, cx| {
             if this.drag_target_channel.take().is_none() {
+                if is_active {
+                    this.open_channel_notes(&OpenChannelNotes { channel_id }, cx)
+                } else {
+                    this.join_channel_call(channel_id, cx)
+                }
+
                 this.join_channel_chat(channel_id, cx);
             }
         })
@@ -2991,10 +3001,37 @@ impl CollabPanel {
             .detach_and_log_err(cx);
     }
 
-    fn join_channel_call(&self, channel: u64, cx: &mut ViewContext<Self>) {
-        ActiveCall::global(cx)
-            .update(cx, |call, cx| call.join_channel(channel, cx))
-            .detach_and_log_err(cx);
+    fn join_channel_call(&self, channel_id: u64, cx: &mut ViewContext<Self>) {
+        let join = ActiveCall::global(cx).update(cx, |call, cx| call.join_channel(channel_id, cx));
+        let workspace = self.workspace.clone();
+
+        cx.spawn(|_, mut cx| async move {
+            let room = join.await?;
+
+            let tasks = room.update(&mut cx, |room, cx| {
+                let Some(workspace) = workspace.upgrade(cx) else {
+                    return vec![];
+                };
+                let projects = room.projects_to_join();
+
+                if projects.is_empty() {
+                    ChannelView::open(channel_id, workspace, cx).detach();
+                    return vec![];
+                }
+                room.projects_to_join()
+                    .into_iter()
+                    .map(|(project_id, user_id)| {
+                        let app_state = workspace.read(cx).app_state().clone();
+                        workspace::join_remote_project(project_id, user_id, app_state, cx)
+                    })
+                    .collect()
+            });
+            for task in tasks {
+                task.await?;
+            }
+            Ok::<(), anyhow::Error>(())
+        })
+        .detach_and_log_err(cx);
     }
 
     fn join_channel_chat(&mut self, channel_id: u64, cx: &mut ViewContext<Self>) {