Start fleshing out layout of collaborator list entries

Julia and Petros Amoiridis created

Co-Authored-By: Petros Amoiridis <petros@zed.dev>

Change summary

crates/collab_ui/src/collaborator_list_popover.rs | 102 +++++++++++++++-
1 file changed, 92 insertions(+), 10 deletions(-)

Detailed changes

crates/collab_ui/src/collaborator_list_popover.rs 🔗

@@ -1,6 +1,9 @@
 use call::ActiveCall;
 use client::UserStore;
-use gpui::{elements::*, Entity, ModelHandle, MouseButton, RenderContext, View, ViewContext};
+use gpui::Action;
+use gpui::{
+    actions, elements::*, Entity, ModelHandle, MouseButton, RenderContext, View, ViewContext,
+};
 use settings::Settings;
 
 use crate::collab_titlebar_item::ToggleCollaboratorList;
@@ -9,6 +12,13 @@ pub(crate) enum Event {
     Dismissed,
 }
 
+enum Collaborator {
+    SelfUser { username: String },
+    RemoteUser { username: String },
+}
+
+actions!(collaborator_list_popover, [NoOp]);
+
 pub(crate) struct CollaboratorListPopover {
     list_state: ListState,
 }
@@ -52,14 +62,18 @@ impl CollaboratorListPopover {
         let mut collaborators = user_store
             .read(cx)
             .current_user()
-            .map(|u| u.github_login.clone())
+            .map(|u| Collaborator::SelfUser {
+                username: u.github_login.clone(),
+            })
             .into_iter()
             .collect::<Vec<_>>();
 
         //TODO: What should the canonical sort here look like, consult contacts list implementation
         if let Some(room) = active_call.read(cx).room() {
             for participant in room.read(cx).remote_participants() {
-                collaborators.push(participant.1.user.github_login.clone());
+                collaborators.push(Collaborator::RemoteUser {
+                    username: participant.1.user.github_login.clone(),
+                });
             }
         }
 
@@ -69,15 +83,83 @@ impl CollaboratorListPopover {
                 Orientation::Top,
                 0.,
                 cx,
-                move |_, index, cx| {
-                    let theme = &cx.global::<Settings>().theme;
-                    Label::new(
-                        collaborators[index].clone(),
-                        theme.contact_list.contact_username.text.clone(),
-                    )
-                    .boxed()
+                move |_, index, cx| match &collaborators[index] {
+                    Collaborator::SelfUser { username } => render_collaborator_list_entry(
+                        index,
+                        username,
+                        None::<NoOp>,
+                        None,
+                        Svg::new("icons/chevron_right_12.svg"),
+                        NoOp,
+                        "Leave call".to_owned(),
+                        cx,
+                    ),
+
+                    Collaborator::RemoteUser { username } => render_collaborator_list_entry(
+                        index,
+                        username,
+                        Some(NoOp),
+                        Some(format!("Follow {username}")),
+                        Svg::new("icons/x_mark_12.svg"),
+                        NoOp,
+                        format!("Remove {username} from call"),
+                        cx,
+                    ),
                 },
             ),
         }
     }
 }
+
+fn render_collaborator_list_entry<UA: Action + Clone, IA: Action + Clone>(
+    index: usize,
+    username: &str,
+    username_action: Option<UA>,
+    username_tooltip: Option<String>,
+    icon: Svg,
+    icon_action: IA,
+    icon_tooltip: String,
+    cx: &mut RenderContext<CollaboratorListPopover>,
+) -> ElementBox {
+    enum Username {}
+    enum UsernameTooltip {}
+    enum Icon {}
+    enum IconTooltip {}
+
+    let theme = &cx.global::<Settings>().theme;
+    let username_theme = theme.contact_list.contact_username.text.clone();
+    let tooltip_theme = theme.tooltip.clone();
+
+    let username = MouseEventHandler::<Username>::new(index, cx, |_, _| {
+        Label::new(username.to_owned(), username_theme.clone()).boxed()
+    })
+    .on_click(MouseButton::Left, move |_, cx| {
+        if let Some(username_action) = username_action.clone() {
+            cx.dispatch_action(username_action);
+        }
+    });
+
+    Flex::row()
+        .with_child(if let Some(username_tooltip) = username_tooltip {
+            username
+                .with_tooltip::<UsernameTooltip, _>(
+                    index,
+                    username_tooltip,
+                    None,
+                    tooltip_theme.clone(),
+                    cx,
+                )
+                .boxed()
+        } else {
+            username.boxed()
+        })
+        .with_child(
+            MouseEventHandler::<Icon>::new(index, cx, |_, _| icon.boxed())
+                .on_click(MouseButton::Left, move |_, cx| {
+                    cx.dispatch_action(icon_action.clone())
+                })
+                .with_tooltip::<IconTooltip, _>(index, icon_tooltip, None, tooltip_theme, cx)
+                .boxed(),
+        )
+        .boxed()
+}