1use call::ActiveCall;
2use client::UserStore;
3use gpui::Action;
4use gpui::{actions, elements::*, platform::MouseButton, Entity, ModelHandle, View, ViewContext};
5use settings::Settings;
6
7use crate::collab_titlebar_item::ToggleCollaboratorList;
8
9pub(crate) enum Event {
10 Dismissed,
11}
12
13enum Collaborator {
14 SelfUser { username: String },
15 RemoteUser { username: String },
16}
17
18actions!(collaborator_list_popover, [NoOp]);
19
20pub(crate) struct CollaboratorListPopover {
21 list_state: ListState<Self>,
22}
23
24impl Entity for CollaboratorListPopover {
25 type Event = Event;
26}
27
28impl View for CollaboratorListPopover {
29 fn ui_name() -> &'static str {
30 "CollaboratorListPopover"
31 }
32
33 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
34 let theme = cx.global::<Settings>().theme.clone();
35
36 MouseEventHandler::<Self, Self>::new(0, cx, |_, _| {
37 List::new(self.list_state.clone())
38 .contained()
39 .with_style(theme.contacts_popover.container) //TODO: Change the name of this theme key
40 .constrained()
41 .with_width(theme.contacts_popover.width)
42 .with_height(theme.contacts_popover.height)
43 })
44 .on_down_out(MouseButton::Left, move |_, _, cx| {
45 cx.dispatch_action(ToggleCollaboratorList);
46 })
47 .into_any()
48 }
49
50 fn focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
51 cx.emit(Event::Dismissed);
52 }
53}
54
55impl CollaboratorListPopover {
56 pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
57 let active_call = ActiveCall::global(cx);
58
59 let mut collaborators = user_store
60 .read(cx)
61 .current_user()
62 .map(|u| Collaborator::SelfUser {
63 username: u.github_login.clone(),
64 })
65 .into_iter()
66 .collect::<Vec<_>>();
67
68 //TODO: What should the canonical sort here look like, consult contacts list implementation
69 if let Some(room) = active_call.read(cx).room() {
70 for participant in room.read(cx).remote_participants() {
71 collaborators.push(Collaborator::RemoteUser {
72 username: participant.1.user.github_login.clone(),
73 });
74 }
75 }
76
77 Self {
78 list_state: ListState::new(
79 collaborators.len(),
80 Orientation::Top,
81 0.,
82 move |_, index, cx| match &collaborators[index] {
83 Collaborator::SelfUser { username } => render_collaborator_list_entry(
84 index,
85 username,
86 None::<NoOp>,
87 None,
88 Svg::new("icons/chevron_right_12.svg"),
89 NoOp,
90 "Leave call".to_owned(),
91 cx,
92 ),
93
94 Collaborator::RemoteUser { username } => render_collaborator_list_entry(
95 index,
96 username,
97 Some(NoOp),
98 Some(format!("Follow {username}")),
99 Svg::new("icons/x_mark_12.svg"),
100 NoOp,
101 format!("Remove {username} from call"),
102 cx,
103 ),
104 },
105 ),
106 }
107 }
108}
109
110fn render_collaborator_list_entry<UA: Action + Clone, IA: Action + Clone>(
111 index: usize,
112 username: &str,
113 username_action: Option<UA>,
114 username_tooltip: Option<String>,
115 icon: Svg,
116 icon_action: IA,
117 icon_tooltip: String,
118 cx: &mut ViewContext<CollaboratorListPopover>,
119) -> AnyElement<CollaboratorListPopover> {
120 enum Username {}
121 enum UsernameTooltip {}
122 enum Icon {}
123 enum IconTooltip {}
124
125 let theme = &cx.global::<Settings>().theme;
126 let username_theme = theme.contact_list.contact_username.text.clone();
127 let tooltip_theme = theme.tooltip.clone();
128
129 let username =
130 MouseEventHandler::<Username, CollaboratorListPopover>::new(index, cx, |_, _| {
131 Label::new(username.to_owned(), username_theme.clone())
132 })
133 .on_click(MouseButton::Left, move |_, _, cx| {
134 if let Some(username_action) = username_action.clone() {
135 cx.dispatch_action(username_action);
136 }
137 });
138
139 Flex::row()
140 .with_child(if let Some(username_tooltip) = username_tooltip {
141 username
142 .with_tooltip::<UsernameTooltip>(
143 index,
144 username_tooltip,
145 None,
146 tooltip_theme.clone(),
147 cx,
148 )
149 .into_any()
150 } else {
151 username.into_any()
152 })
153 .with_child(
154 MouseEventHandler::<Icon, CollaboratorListPopover>::new(index, cx, |_, _| icon)
155 .on_click(MouseButton::Left, move |_, _, cx| {
156 cx.dispatch_action(icon_action.clone())
157 })
158 .with_tooltip::<IconTooltip>(index, icon_tooltip, None, tooltip_theme, cx),
159 )
160 .into_any()
161}