1use call::{room, ActiveCall};
2use client::User;
3use gpui::{
4 actions,
5 elements::*,
6 geometry::{rect::RectF, vector::vec2f},
7 CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext,
8 WindowBounds, WindowKind, WindowOptions,
9};
10use settings::Settings;
11use std::sync::Arc;
12use workspace::JoinProject;
13
14actions!(project_shared_notification, [DismissProject]);
15
16pub fn init(cx: &mut MutableAppContext) {
17 cx.add_action(ProjectSharedNotification::join);
18 cx.add_action(ProjectSharedNotification::dismiss);
19
20 let active_call = ActiveCall::global(cx);
21 cx.subscribe(&active_call, move |_, event, cx| match event {
22 room::Event::RemoteProjectShared {
23 owner,
24 project_id,
25 worktree_root_names,
26 } => {
27 const PADDING: f32 = 16.;
28 let screen_size = cx.platform().screen_size();
29
30 let theme = &cx.global::<Settings>().theme.project_shared_notification;
31 let window_size = vec2f(theme.window_width, theme.window_height);
32 cx.add_window(
33 WindowOptions {
34 bounds: WindowBounds::Fixed(RectF::new(
35 vec2f(screen_size.x() - window_size.x() - PADDING, PADDING),
36 window_size,
37 )),
38 titlebar: None,
39 center: false,
40 kind: WindowKind::PopUp,
41 is_movable: false,
42 },
43 |_| {
44 ProjectSharedNotification::new(
45 owner.clone(),
46 *project_id,
47 worktree_root_names.clone(),
48 )
49 },
50 );
51 }
52 })
53 .detach();
54}
55
56pub struct ProjectSharedNotification {
57 project_id: u64,
58 worktree_root_names: Vec<String>,
59 owner: Arc<User>,
60}
61
62impl ProjectSharedNotification {
63 fn new(owner: Arc<User>, project_id: u64, worktree_root_names: Vec<String>) -> Self {
64 Self {
65 project_id,
66 worktree_root_names,
67 owner,
68 }
69 }
70
71 fn join(&mut self, _: &JoinProject, cx: &mut ViewContext<Self>) {
72 let window_id = cx.window_id();
73 cx.remove_window(window_id);
74 cx.propagate_action();
75 }
76
77 fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext<Self>) {
78 let window_id = cx.window_id();
79 cx.remove_window(window_id);
80 }
81
82 fn render_owner(&self, cx: &mut RenderContext<Self>) -> ElementBox {
83 let theme = &cx.global::<Settings>().theme.project_shared_notification;
84 Flex::row()
85 .with_children(self.owner.avatar.clone().map(|avatar| {
86 Image::new(avatar)
87 .with_style(theme.owner_avatar)
88 .aligned()
89 .boxed()
90 }))
91 .with_child(
92 Flex::column()
93 .with_child(
94 Label::new(
95 self.owner.github_login.clone(),
96 theme.owner_username.text.clone(),
97 )
98 .contained()
99 .with_style(theme.owner_username.container)
100 .boxed(),
101 )
102 .with_child(
103 Label::new(
104 format!(
105 "is sharing a project in Zed{}",
106 if self.worktree_root_names.is_empty() {
107 ""
108 } else {
109 ":"
110 }
111 ),
112 theme.message.text.clone(),
113 )
114 .contained()
115 .with_style(theme.message.container)
116 .boxed(),
117 )
118 .with_children(if self.worktree_root_names.is_empty() {
119 None
120 } else {
121 Some(
122 Label::new(
123 self.worktree_root_names.join(", "),
124 theme.worktree_roots.text.clone(),
125 )
126 .contained()
127 .with_style(theme.worktree_roots.container)
128 .boxed(),
129 )
130 })
131 .contained()
132 .with_style(theme.owner_metadata)
133 .aligned()
134 .boxed(),
135 )
136 .contained()
137 .with_style(theme.owner_container)
138 .flex(1., true)
139 .boxed()
140 }
141
142 fn render_buttons(&self, cx: &mut RenderContext<Self>) -> ElementBox {
143 enum Open {}
144 enum Dismiss {}
145
146 let project_id = self.project_id;
147 let owner_user_id = self.owner.id;
148
149 Flex::column()
150 .with_child(
151 MouseEventHandler::<Open>::new(0, cx, |_, cx| {
152 let theme = &cx.global::<Settings>().theme.project_shared_notification;
153 Label::new("Open".to_string(), theme.open_button.text.clone())
154 .aligned()
155 .contained()
156 .with_style(theme.open_button.container)
157 .boxed()
158 })
159 .with_cursor_style(CursorStyle::PointingHand)
160 .on_click(MouseButton::Left, move |_, cx| {
161 cx.dispatch_action(JoinProject {
162 project_id,
163 follow_user_id: owner_user_id,
164 });
165 })
166 .flex(1., true)
167 .boxed(),
168 )
169 .with_child(
170 MouseEventHandler::<Dismiss>::new(0, cx, |_, cx| {
171 let theme = &cx.global::<Settings>().theme.project_shared_notification;
172 Label::new("Dismiss".to_string(), theme.dismiss_button.text.clone())
173 .aligned()
174 .contained()
175 .with_style(theme.dismiss_button.container)
176 .boxed()
177 })
178 .with_cursor_style(CursorStyle::PointingHand)
179 .on_click(MouseButton::Left, |_, cx| {
180 cx.dispatch_action(DismissProject);
181 })
182 .flex(1., true)
183 .boxed(),
184 )
185 .constrained()
186 .with_width(
187 cx.global::<Settings>()
188 .theme
189 .project_shared_notification
190 .button_width,
191 )
192 .boxed()
193 }
194}
195
196impl Entity for ProjectSharedNotification {
197 type Event = ();
198}
199
200impl View for ProjectSharedNotification {
201 fn ui_name() -> &'static str {
202 "ProjectSharedNotification"
203 }
204
205 fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
206 let background = cx
207 .global::<Settings>()
208 .theme
209 .project_shared_notification
210 .background;
211 Flex::row()
212 .with_child(self.render_owner(cx))
213 .with_child(self.render_buttons(cx))
214 .contained()
215 .with_background_color(background)
216 .expanded()
217 .boxed()
218 }
219}