Hide chat panel button when not in a call (#22200)

Cole Miller created

cc @nathansobo 

Release Notes:

- Hide chat panel button by default when not in a call

Change summary

assets/settings/default.json           |  6 +
crates/collab_ui/src/chat_panel.rs     | 11 +++
crates/collab_ui/src/collab_ui.rs      |  2 
crates/collab_ui/src/panel_settings.rs | 75 ++++++++++++++++++++++++++-
4 files changed, 86 insertions(+), 8 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -481,8 +481,10 @@
     "default_width": 240
   },
   "chat_panel": {
-    // Whether to show the chat panel button in the status bar.
-    "button": true,
+    // When to show the chat panel button in the status bar.
+    // Can be 'never', 'always', or 'when_in_call',
+    // or a boolean (interpreted as 'never'/'always').
+    "button": "when_in_call",
     // Where to the chat panel. Can be 'left' or 'right'.
     "dock": "right",
     // Default width of the chat panel.

crates/collab_ui/src/chat_panel.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{collab_panel, ChatPanelSettings};
+use crate::{collab_panel, ChatPanelButton, ChatPanelSettings};
 use anyhow::Result;
 use call::{room, ActiveCall};
 use channel::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, ChannelStore};
@@ -1135,7 +1135,14 @@ impl Panel for ChatPanel {
     }
 
     fn icon(&self, cx: &WindowContext) -> Option<ui::IconName> {
-        Some(ui::IconName::MessageBubbles).filter(|_| ChatPanelSettings::get_global(cx).button)
+        match ChatPanelSettings::get_global(cx).button {
+            ChatPanelButton::Never => None,
+            ChatPanelButton::Always => Some(ui::IconName::MessageBubbles),
+            ChatPanelButton::WhenInCall => ActiveCall::global(cx)
+                .read(cx)
+                .room()
+                .map(|_| ui::IconName::MessageBubbles),
+        }
     }
 
     fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {

crates/collab_ui/src/collab_ui.rs 🔗

@@ -14,7 +14,7 @@ use gpui::{
 };
 use panel_settings::MessageEditorSettings;
 pub use panel_settings::{
-    ChatPanelSettings, CollaborationPanelSettings, NotificationPanelSettings,
+    ChatPanelButton, ChatPanelSettings, CollaborationPanelSettings, NotificationPanelSettings,
 };
 use release_channel::ReleaseChannel;
 use settings::Settings;

crates/collab_ui/src/panel_settings.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::Pixels;
 use schemars::JsonSchema;
-use serde_derive::{Deserialize, Serialize};
+use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsSources};
 use workspace::dock::DockPosition;
 
@@ -11,13 +11,82 @@ pub struct CollaborationPanelSettings {
     pub default_width: Pixels,
 }
 
+#[derive(Clone, Copy, Default, Serialize, JsonSchema, Debug)]
+#[serde(rename_all = "snake_case")]
+pub enum ChatPanelButton {
+    Never,
+    Always,
+    #[default]
+    WhenInCall,
+}
+
+impl<'de> Deserialize<'de> for ChatPanelButton {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        struct Visitor;
+
+        impl<'de> serde::de::Visitor<'de> for Visitor {
+            type Value = ChatPanelButton;
+
+            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                write!(
+                    f,
+                    r#"a boolean or one of "never", "always", "when_in_call""#
+                )
+            }
+
+            fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                match b {
+                    false => Ok(ChatPanelButton::Never),
+                    true => Ok(ChatPanelButton::Always),
+                }
+            }
+
+            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                match s {
+                    "never" => Ok(ChatPanelButton::Never),
+                    "always" => Ok(ChatPanelButton::Always),
+                    "when_in_call" => Ok(ChatPanelButton::WhenInCall),
+                    _ => Err(E::unknown_variant(s, &["never", "always", "when_in_call"])),
+                }
+            }
+        }
+
+        deserializer.deserialize_any(Visitor)
+    }
+}
+
 #[derive(Deserialize, Debug)]
 pub struct ChatPanelSettings {
-    pub button: bool,
+    pub button: ChatPanelButton,
     pub dock: DockPosition,
     pub default_width: Pixels,
 }
 
+#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
+pub struct ChatPanelSettingsContent {
+    /// When to show the panel button in the status bar.
+    ///
+    /// Default: only when in a call
+    pub button: Option<ChatPanelButton>,
+    /// Where to dock the panel.
+    ///
+    /// Default: right
+    pub dock: Option<DockPosition>,
+    /// Default width of the panel in pixels.
+    ///
+    /// Default: 240
+    pub default_width: Option<f32>,
+}
+
 #[derive(Deserialize, Debug)]
 pub struct NotificationPanelSettings {
     pub button: bool,
@@ -66,7 +135,7 @@ impl Settings for CollaborationPanelSettings {
 impl Settings for ChatPanelSettings {
     const KEY: Option<&'static str> = Some("chat_panel");
 
-    type FileContent = PanelSettingsContent;
+    type FileContent = ChatPanelSettingsContent;
 
     fn load(
         sources: SettingsSources<Self::FileContent>,