1use crate::notification_window_options;
2use crate::notifications::collab_notification::CollabNotification;
3use call::{ActiveCall, IncomingCall};
4use futures::StreamExt;
5use gpui::{App, WindowHandle, prelude::*};
6
7use std::sync::{Arc, Weak};
8use ui::{Button, Label, prelude::*};
9use util::ResultExt;
10use workspace::AppState;
11
12pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
13 let app_state = Arc::downgrade(app_state);
14 let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
15 cx.spawn(async move |cx| {
16 let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
17 while let Some(incoming_call) = incoming_call.next().await {
18 for window in notification_windows.drain(..) {
19 window
20 .update(cx, |_, window, _| {
21 window.remove_window();
22 })
23 .log_err();
24 }
25
26 if let Some(incoming_call) = incoming_call {
27 let unique_screens = cx.update(|cx| cx.displays()).unwrap();
28 let window_size = gpui::Size {
29 width: px(400.),
30 height: px(72.),
31 };
32
33 for screen in unique_screens {
34 if let Some(options) = cx
35 .update(|cx| notification_window_options(screen, window_size, cx))
36 .log_err()
37 {
38 let window = cx
39 .open_window(options, |_, cx| {
40 cx.new(|_| {
41 IncomingCallNotification::new(
42 incoming_call.clone(),
43 app_state.clone(),
44 )
45 })
46 })
47 .unwrap();
48 notification_windows.push(window);
49 }
50 }
51 }
52 }
53 })
54 .detach();
55}
56
57struct IncomingCallNotificationState {
58 call: IncomingCall,
59 app_state: Weak<AppState>,
60}
61
62pub struct IncomingCallNotification {
63 state: Arc<IncomingCallNotificationState>,
64}
65impl IncomingCallNotificationState {
66 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
67 Self { call, app_state }
68 }
69
70 fn respond(&self, accept: bool, cx: &mut App) {
71 let active_call = ActiveCall::global(cx);
72 if accept {
73 let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
74 let caller_user_id = self.call.calling_user.id;
75 let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
76 let app_state = self.app_state.clone();
77 let cx: &mut App = cx;
78 cx.spawn(async move |cx| {
79 join.await?;
80 if let Some(project_id) = initial_project_id {
81 cx.update(|cx| {
82 if let Some(app_state) = app_state.upgrade() {
83 workspace::join_in_room_project(
84 project_id,
85 caller_user_id,
86 app_state,
87 cx,
88 )
89 .detach_and_log_err(cx);
90 }
91 })
92 .log_err();
93 }
94 anyhow::Ok(())
95 })
96 .detach_and_log_err(cx);
97 } else {
98 active_call.update(cx, |active_call, cx| {
99 active_call.decline_incoming(cx).log_err();
100 });
101 }
102 }
103}
104
105impl IncomingCallNotification {
106 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
107 Self {
108 state: Arc::new(IncomingCallNotificationState::new(call, app_state)),
109 }
110 }
111}
112
113impl Render for IncomingCallNotification {
114 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
115 let ui_font = theme::setup_ui_font(window, cx);
116
117 div().size_full().font(ui_font).child(
118 CollabNotification::new(
119 self.state.call.calling_user.avatar_uri.clone(),
120 Button::new("accept", "Accept").on_click({
121 let state = self.state.clone();
122 move |_, _, cx| state.respond(true, cx)
123 }),
124 Button::new("decline", "Decline").on_click({
125 let state = self.state.clone();
126 move |_, _, cx| state.respond(false, cx)
127 }),
128 )
129 .child(v_flex().overflow_hidden().child(Label::new(format!(
130 "{} is sharing a project in Zed",
131 self.state.call.calling_user.github_login
132 )))),
133 )
134 }
135}