1use crate::notification_window_options;
2use call::{ActiveCall, IncomingCall};
3use futures::StreamExt;
4use gpui::{
5 div, px, red, AppContext, Div, Element, ParentElement, Render, RenderOnce, Styled, ViewContext,
6 VisualContext as _, WindowHandle,
7};
8use std::sync::{Arc, Weak};
9use ui::prelude::*;
10use ui::{h_stack, v_stack, Avatar, Button, Label};
11use util::ResultExt;
12use workspace::AppState;
13
14pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
15 let app_state = Arc::downgrade(app_state);
16 let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
17 cx.spawn(|mut cx| async move {
18 let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
19 while let Some(incoming_call) = incoming_call.next().await {
20 for window in notification_windows.drain(..) {
21 window
22 .update(&mut cx, |_, cx| {
23 // todo!()
24 cx.remove_window();
25 })
26 .log_err();
27 }
28
29 if let Some(incoming_call) = incoming_call {
30 let unique_screens = cx.update(|cx| cx.displays()).unwrap();
31 let window_size = gpui::Size {
32 width: px(380.),
33 height: px(64.),
34 };
35
36 for window in unique_screens {
37 let options = notification_window_options(window, window_size);
38 let window = cx
39 .open_window(options, |cx| {
40 cx.build_view(|_| {
41 IncomingCallNotification::new(
42 incoming_call.clone(),
43 app_state.clone(),
44 )
45 })
46 })
47 .unwrap();
48 notification_windows.push(window);
49 }
50
51 // for screen in cx.platform().screens() {
52 // let window = cx
53 // .add_window(notification_window_options(screen, window_size), |_| {
54 // IncomingCallNotification::new(incoming_call.clone(), app_state.clone())
55 // });
56
57 // notification_windows.push(window);
58 // }
59 }
60 }
61 })
62 .detach();
63}
64
65#[derive(Clone, PartialEq)]
66struct RespondToCall {
67 accept: bool,
68}
69
70struct IncomingCallNotificationState {
71 call: IncomingCall,
72 app_state: Weak<AppState>,
73}
74
75pub struct IncomingCallNotification {
76 state: Arc<IncomingCallNotificationState>,
77}
78impl IncomingCallNotificationState {
79 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
80 Self { call, app_state }
81 }
82
83 fn respond(&self, accept: bool, cx: &mut AppContext) {
84 let active_call = ActiveCall::global(cx);
85 if accept {
86 let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
87 let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
88 let app_state = self.app_state.clone();
89 let cx: &mut AppContext = cx;
90 cx.spawn(|cx| async move {
91 join.await?;
92 if let Some(_project_id) = initial_project_id {
93 cx.update(|_cx| {
94 if let Some(_app_state) = app_state.upgrade() {
95 // workspace::join_remote_project(
96 // project_id,
97 // caller_user_id,
98 // app_state,
99 // cx,
100 // )
101 // .detach_and_log_err(cx);
102 }
103 })
104 .log_err();
105 }
106 anyhow::Ok(())
107 })
108 .detach_and_log_err(cx);
109 } else {
110 active_call.update(cx, |active_call, cx| {
111 active_call.decline_incoming(cx).log_err();
112 });
113 }
114 }
115}
116
117impl IncomingCallNotification {
118 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
119 Self {
120 state: Arc::new(IncomingCallNotificationState::new(call, app_state)),
121 }
122 }
123 fn render_caller(&self, cx: &mut ViewContext<Self>) -> impl Element {
124 h_stack()
125 .children(
126 self.state
127 .call
128 .calling_user
129 .avatar
130 .as_ref()
131 .map(|avatar| Avatar::data(avatar.clone())),
132 )
133 .child(
134 v_stack()
135 .child(Label::new(format!(
136 "{} is sharing a project in Zed",
137 self.state.call.calling_user.github_login
138 )))
139 .child(self.render_buttons(cx)),
140 )
141 // let theme = &theme::current(cx).incoming_call_notification;
142 // let default_project = proto::ParticipantProject::default();
143 // let initial_project = self
144 // .call
145 // .initial_project
146 // .as_ref()
147 // .unwrap_or(&default_project);
148 // Flex::row()
149 // .with_children(self.call.calling_user.avatar.clone().map(|avatar| {
150 // Image::from_data(avatar)
151 // .with_style(theme.caller_avatar)
152 // .aligned()
153 // }))
154 // .with_child(
155 // Flex::column()
156 // .with_child(
157 // Label::new(
158 // self.call.calling_user.github_login.clone(),
159 // theme.caller_username.text.clone(),
160 // )
161 // .contained()
162 // .with_style(theme.caller_username.container),
163 // )
164 // .with_child(
165 // Label::new(
166 // format!(
167 // "is sharing a project in Zed{}",
168 // if initial_project.worktree_root_names.is_empty() {
169 // ""
170 // } else {
171 // ":"
172 // }
173 // ),
174 // theme.caller_message.text.clone(),
175 // )
176 // .contained()
177 // .with_style(theme.caller_message.container),
178 // )
179 // .with_children(if initial_project.worktree_root_names.is_empty() {
180 // None
181 // } else {
182 // Some(
183 // Label::new(
184 // initial_project.worktree_root_names.join(", "),
185 // theme.worktree_roots.text.clone(),
186 // )
187 // .contained()
188 // .with_style(theme.worktree_roots.container),
189 // )
190 // })
191 // .contained()
192 // .with_style(theme.caller_metadata)
193 // .aligned(),
194 // )
195 // .contained()
196 // .with_style(theme.caller_container)
197 // .flex(1., true)
198 // .into_any()
199 }
200
201 fn render_buttons(&self, cx: &mut ViewContext<Self>) -> impl Element {
202 h_stack()
203 .child(
204 Button::new("accept", "Accept")
205 .render(cx)
206 // .bg(green())
207 .on_click({
208 let state = self.state.clone();
209 move |_, cx| state.respond(true, cx)
210 }),
211 )
212 .child(
213 Button::new("decline", "Decline")
214 .render(cx)
215 // .bg(red())
216 .on_click({
217 let state = self.state.clone();
218 move |_, cx| state.respond(false, cx)
219 }),
220 )
221
222 // enum Accept {}
223 // enum Decline {}
224
225 // let theme = theme::current(cx);
226 // Flex::column()
227 // .with_child(
228 // MouseEventHandler::new::<Accept, _>(0, cx, |_, _| {
229 // let theme = &theme.incoming_call_notification;
230 // Label::new("Accept", theme.accept_button.text.clone())
231 // .aligned()
232 // .contained()
233 // .with_style(theme.accept_button.container)
234 // })
235 // .with_cursor_style(CursorStyle::PointingHand)
236 // .on_click(MouseButton::Left, |_, this, cx| {
237 // this.respond(true, cx);
238 // })
239 // .flex(1., true),
240 // )
241 // .with_child(
242 // MouseEventHandler::new::<Decline, _>(0, cx, |_, _| {
243 // let theme = &theme.incoming_call_notification;
244 // Label::new("Decline", theme.decline_button.text.clone())
245 // .aligned()
246 // .contained()
247 // .with_style(theme.decline_button.container)
248 // })
249 // .with_cursor_style(CursorStyle::PointingHand)
250 // .on_click(MouseButton::Left, |_, this, cx| {
251 // this.respond(false, cx);
252 // })
253 // .flex(1., true),
254 // )
255 // .constrained()
256 // .with_width(theme.incoming_call_notification.button_width)
257 // .into_any()
258 }
259}
260impl Render for IncomingCallNotification {
261 type Element = Div;
262 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
263 div().bg(red()).flex_none().child(self.render_caller(cx))
264 // Flex::row()
265 // .with_child()
266 // .with_child(self.render_buttons(cx))
267 // .contained()
268 // .with_background_color(background)
269 // .expanded()
270 // .into_any()
271 }
272}