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>) -> Element<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 .boxed()
44 })
45 .on_down_out(MouseButton::Left, move |_, _, cx| {
46 cx.dispatch_action(ToggleCollaboratorList);
47 })
48 .boxed()
49 }
50
51 fn focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
52 cx.emit(Event::Dismissed);
53 }
54}
55
56impl CollaboratorListPopover {
57 pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
58 let active_call = ActiveCall::global(cx);
59
60 let mut collaborators = user_store
61 .read(cx)
62 .current_user()
63 .map(|u| Collaborator::SelfUser {
64 username: u.github_login.clone(),
65 })
66 .into_iter()
67 .collect::<Vec<_>>();
68
69 //TODO: What should the canonical sort here look like, consult contacts list implementation
70 if let Some(room) = active_call.read(cx).room() {
71 for participant in room.read(cx).remote_participants() {
72 collaborators.push(Collaborator::RemoteUser {
73 username: participant.1.user.github_login.clone(),
74 });
75 }
76 }
77
78 Self {
79 list_state: ListState::new(
80 collaborators.len(),
81 Orientation::Top,
82 0.,
83 move |_, index, cx| match &collaborators[index] {
84 Collaborator::SelfUser { username } => render_collaborator_list_entry(
85 index,
86 username,
87 None::<NoOp>,
88 None,
89 Svg::new("icons/chevron_right_12.svg"),
90 NoOp,
91 "Leave call".to_owned(),
92 cx,
93 ),
94
95 Collaborator::RemoteUser { username } => render_collaborator_list_entry(
96 index,
97 username,
98 Some(NoOp),
99 Some(format!("Follow {username}")),
100 Svg::new("icons/x_mark_12.svg"),
101 NoOp,
102 format!("Remove {username} from call"),
103 cx,
104 ),
105 },
106 ),
107 }
108 }
109}
110
111fn render_collaborator_list_entry<UA: Action + Clone, IA: Action + Clone>(
112 index: usize,
113 username: &str,
114 username_action: Option<UA>,
115 username_tooltip: Option<String>,
116 icon: Svg,
117 icon_action: IA,
118 icon_tooltip: String,
119 cx: &mut ViewContext<CollaboratorListPopover>,
120) -> Element<CollaboratorListPopover> {
121 enum Username {}
122 enum UsernameTooltip {}
123 enum Icon {}
124 enum IconTooltip {}
125
126 let theme = &cx.global::<Settings>().theme;
127 let username_theme = theme.contact_list.contact_username.text.clone();
128 let tooltip_theme = theme.tooltip.clone();
129
130 let username =
131 MouseEventHandler::<Username, CollaboratorListPopover>::new(index, cx, |_, _| {
132 Label::new(username.to_owned(), username_theme.clone()).boxed()
133 })
134 .on_click(MouseButton::Left, move |_, _, cx| {
135 if let Some(username_action) = username_action.clone() {
136 cx.dispatch_action(username_action);
137 }
138 });
139
140 Flex::row()
141 .with_child(if let Some(username_tooltip) = username_tooltip {
142 username
143 .with_tooltip::<UsernameTooltip>(
144 index,
145 username_tooltip,
146 None,
147 tooltip_theme.clone(),
148 cx,
149 )
150 .boxed()
151 } else {
152 username.boxed()
153 })
154 .with_child(
155 MouseEventHandler::<Icon, CollaboratorListPopover>::new(index, cx, |_, _| icon.boxed())
156 .on_click(MouseButton::Left, move |_, _, cx| {
157 cx.dispatch_action(icon_action.clone())
158 })
159 .with_tooltip::<IconTooltip>(index, icon_tooltip, None, tooltip_theme, cx)
160 .boxed(),
161 )
162 .boxed()
163}