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