Add mute handling

Piotr Osiewicz created

Change summary

crates/call2/src/call2.rs                     | 37 ++++++++++
crates/call2/src/room.rs                      |  3 
crates/client2/src/client2.rs                 |  1 
crates/collab_ui2/src/collab_titlebar_item.rs | 77 ++++++++++++++++----
crates/workspace2/src/workspace2.rs           | 25 ++++++
5 files changed, 122 insertions(+), 21 deletions(-)

Detailed changes

crates/call2/src/call2.rs 🔗

@@ -17,6 +17,7 @@ use gpui::{
     Subscription, Task, View, ViewContext, WeakModel, WeakView,
 };
 pub use participant::ParticipantLocation;
+use participant::RemoteParticipant;
 use postage::watch;
 use project::Project;
 use room::Event;
@@ -628,6 +629,42 @@ impl CallHandler for Call {
             this.invite(called_user_id, initial_project, cx)
         })
     }
+    fn remote_participants(&self, cx: &AppContext) -> Option<Vec<Arc<User>>> {
+        self.active_call
+            .as_ref()
+            .map(|call| {
+                call.0.read(cx).room().map(|room| {
+                    room.read(cx)
+                        .remote_participants()
+                        .iter()
+                        .map(|participant| participant.1.user.clone())
+                        .collect()
+                })
+            })
+            .flatten()
+    }
+    fn is_muted(&self, cx: &AppContext) -> Option<bool> {
+        self.active_call
+            .as_ref()
+            .map(|call| {
+                call.0
+                    .read(cx)
+                    .room()
+                    .map(|room| room.read(cx).is_muted(cx))
+            })
+            .flatten()
+    }
+    fn toggle_mute(&self, cx: &mut AppContext) {
+        self.active_call.as_ref().map(|call| {
+            call.0.update(cx, |this, cx| {
+                this.room().map(|room| {
+                    room.update(cx, |this, cx| {
+                        this.toggle_mute(cx);
+                    })
+                })
+            })
+        });
+    }
 }
 
 #[cfg(test)]

crates/call2/src/room.rs 🔗

@@ -333,7 +333,8 @@ impl Room {
     }
 
     pub fn mute_on_join(cx: &AppContext) -> bool {
-        CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some()
+        false
+        //CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some()
     }
 
     fn from_join_response(

crates/client2/src/client2.rs 🔗

@@ -551,7 +551,6 @@ impl Client {
         F: 'static + Future<Output = Result<()>>,
     {
         let message_type_id = TypeId::of::<M>();
-
         let mut state = self.state.write();
         state
             .models_by_message_type

crates/collab_ui2/src/collab_titlebar_item.rs 🔗

@@ -37,7 +37,8 @@ use gpui::{
 };
 use project::Project;
 use theme::ActiveTheme;
-use ui::{h_stack, Button, ButtonVariant, Color, IconButton, KeyBinding, Tooltip};
+use ui::{h_stack, Avatar, Button, ButtonVariant, Color, IconButton, KeyBinding, Label, Tooltip};
+use util::ResultExt;
 use workspace::Workspace;
 
 // const MAX_PROJECT_NAME_LENGTH: usize = 40;
@@ -92,6 +93,23 @@ impl Render for CollabTitlebarItem {
         let is_shared = is_in_room && self.project.read(cx).is_shared();
         let current_user = self.user_store.read(cx).current_user();
         let client = self.client.clone();
+        let users = self
+            .workspace
+            .update(cx, |this, cx| this.call_state().remote_participants(cx))
+            .log_err()
+            .flatten();
+        let mic_icon = if self
+            .workspace
+            .update(cx, |this, cx| this.call_state().is_muted(cx))
+            .log_err()
+            .flatten()
+            .unwrap_or_default()
+        {
+            ui::Icon::MicMute
+        } else {
+            ui::Icon::Mic
+        };
+        let workspace = self.workspace.clone();
         h_stack()
             .id("titlebar")
             .justify_between()
@@ -157,22 +175,23 @@ impl Render for CollabTitlebarItem {
                             }),
                     ),
             )
-            .map(|this| {
-                if let Some(user) = current_user {
-                    this.when_some(user.avatar.clone(), |this, avatar| {
-                        this.child(ui::Avatar::new(avatar))
-                    })
-                } else {
-                    this.child(Button::new("Sign in").on_click(move |_, cx| {
-                        let client = client.clone();
-                        cx.spawn(move |cx| async move {
-                            client.authenticate_and_connect(true, &cx).await?;
-                            Ok::<(), anyhow::Error>(())
-                        })
-                        .detach_and_log_err(cx);
-                    }))
-                }
-            })
+            .when_some(
+                users.zip(current_user.clone()),
+                |this, (remote_participants, current_user)| {
+                    this.children(
+                        current_user
+                            .avatar
+                            .clone()
+                            .map(|avatar| Avatar::new(avatar.clone()))
+                            .into_iter()
+                            .chain(remote_participants.into_iter().flat_map(|user| {
+                                user.avatar
+                                    .as_ref()
+                                    .map(|avatar| Avatar::new(avatar.clone()))
+                            })),
+                    )
+                },
+            )
             .when(is_in_room, |this| {
                 this.child(
                     h_stack()
@@ -183,13 +202,35 @@ impl Render for CollabTitlebarItem {
                         )
                         .child(
                             h_stack()
-                                .child(IconButton::new("mute-microphone", ui::Icon::Mic))
+                                .child(IconButton::new("mute-microphone", mic_icon).on_click(
+                                    move |_, cx| {
+                                        workspace.update(cx, |this, cx| {
+                                            this.call_state().toggle_mute(cx);
+                                        });
+                                    },
+                                ))
                                 .child(IconButton::new("mute-sound", ui::Icon::AudioOn))
                                 .child(IconButton::new("screen-share", ui::Icon::Screen))
                                 .pl_2(),
                         ),
                 )
             })
+            .map(|this| {
+                if let Some(user) = current_user {
+                    this.when_some(user.avatar.clone(), |this, avatar| {
+                        this.child(ui::Avatar::new(avatar))
+                    })
+                } else {
+                    this.child(Button::new("Sign in").on_click(move |_, cx| {
+                        let client = client.clone();
+                        cx.spawn(move |cx| async move {
+                            client.authenticate_and_connect(true, &cx).await?;
+                            Ok::<(), anyhow::Error>(())
+                        })
+                        .detach_and_log_err(cx);
+                    }))
+                }
+            })
     }
 }
 

crates/workspace2/src/workspace2.rs 🔗

@@ -19,7 +19,7 @@ use anyhow::{anyhow, Context as _, Result};
 use async_trait::async_trait;
 use client2::{
     proto::{self, PeerId},
-    Client, TypedEnvelope, UserStore,
+    Client, TypedEnvelope, User, UserStore,
 };
 use collections::{hash_map, HashMap, HashSet};
 use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
@@ -351,7 +351,27 @@ impl CallHandler for TestCallHandler {
     fn active_project(&self, cx: &AppContext) -> Option<WeakModel<Project>> {
         None
     }
+
+    fn invite(
+        &mut self,
+        called_user_id: u64,
+        initial_project: Option<Model<Project>>,
+        cx: &mut AppContext,
+    ) -> Task<Result<()>> {
+        unimplemented!()
+    }
+
+    fn remote_participants(&self, cx: &AppContext) -> Option<Vec<User>> {
+        None
+    }
+
+    fn is_muted(&self, cx: &AppContext) -> Option<bool> {
+        None
+    }
+
+    fn toggle_mute(&self, cx: &mut AppContext) {}
 }
+
 impl AppState {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test(cx: &mut AppContext) -> Arc<Self> {
@@ -460,6 +480,9 @@ pub trait CallHandler {
         initial_project: Option<Model<Project>>,
         cx: &mut AppContext,
     ) -> Task<Result<()>>;
+    fn remote_participants(&self, cx: &AppContext) -> Option<Vec<Arc<User>>>;
+    fn is_muted(&self, cx: &AppContext) -> Option<bool>;
+    fn toggle_mute(&self, cx: &mut AppContext);
 }
 
 pub struct Workspace {