collab_ui: Refine channel tab appearance (#14736)

Marshall Bowers created

This PR refines the appearance of the channel tabs.

We now display the channel icon in the tab's icon slot. We also now
adjust the icon based on whether the channel is public or members-only
(the same we do in the channel list):

<img width="214" alt="Screenshot 2024-07-18 at 9 02 00 AM"
src="https://github.com/user-attachments/assets/973d83c5-f045-4282-a43a-18e12ce93f78">

The `read-only` and `disconnected` states are now also shown in a
visually different style than the channel name:

<img width="247" alt="Screenshot 2024-07-18 at 9 01 13 AM"
src="https://github.com/user-attachments/assets/359f61cf-3b80-4a3f-8948-d705f6c24695">

Release Notes:

- Refined the appearance of channel tabs.

Change summary

crates/collab_ui/src/channel_view.rs | 51 ++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 9 deletions(-)

Detailed changes

crates/collab_ui/src/channel_view.rs 🔗

@@ -16,12 +16,14 @@ use gpui::{
     WindowContext,
 };
 use project::Project;
+use rpc::proto::ChannelVisibility;
 use std::{
     any::{Any, TypeId},
     sync::Arc,
 };
 use ui::prelude::*;
 use util::ResultExt;
+use workspace::item::TabContentParams;
 use workspace::{item::Dedup, notifications::NotificationId};
 use workspace::{
     item::{FollowableItem, Item, ItemEvent, ItemHandle},
@@ -385,20 +387,51 @@ impl Item for ChannelView {
         }
     }
 
-    fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
-        let label = if let Some(channel) = self.channel(cx) {
-            match (
+    fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
+        let channel = self.channel(cx)?;
+        let icon = match channel.visibility {
+            ChannelVisibility::Public => IconName::Public,
+            ChannelVisibility::Members => IconName::Hash,
+        };
+
+        Some(Icon::new(icon))
+    }
+
+    fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> gpui::AnyElement {
+        let (channel_name, status) = if let Some(channel) = self.channel(cx) {
+            let status = match (
                 self.channel_buffer.read(cx).buffer().read(cx).read_only(),
                 self.channel_buffer.read(cx).is_connected(),
             ) {
-                (false, true) => format!("#{}", channel.name),
-                (true, true) => format!("#{} (read-only)", channel.name),
-                (_, false) => format!("#{} (disconnected)", channel.name),
-            }
+                (false, true) => None,
+                (true, true) => Some("read-only"),
+                (_, false) => Some("disconnected"),
+            };
+
+            (channel.name.clone(), status)
         } else {
-            "channel notes (disconnected)".to_string()
+            ("<unknown>".into(), Some("disconnected"))
         };
-        Some(label.into())
+
+        h_flex()
+            .gap_2()
+            .child(
+                Label::new(channel_name)
+                    .color(if params.selected {
+                        Color::Default
+                    } else {
+                        Color::Muted
+                    })
+                    .italic(params.preview),
+            )
+            .when_some(status, |element, status| {
+                element.child(
+                    Label::new(status)
+                        .size(LabelSize::XSmall)
+                        .color(Color::Muted),
+                )
+            })
+            .into_any_element()
     }
 
     fn telemetry_event_text(&self) -> Option<&'static str> {