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());
28 let window_size = gpui::Size {
29 width: px(400.),
30 height: px(72.),
31 };
32
33 for screen in unique_screens {
34 let options =
35 cx.update(|cx| notification_window_options(screen, window_size, cx));
36 if let Ok(window) = cx.open_window(options, |_, cx| {
37 cx.new(|_| {
38 IncomingCallNotification::new(incoming_call.clone(), app_state.clone())
39 })
40 }) {
41 notification_windows.push(window);
42 }
43 }
44 }
45 }
46 })
47 .detach();
48}
49
50struct IncomingCallNotificationState {
51 call: IncomingCall,
52 app_state: Weak<AppState>,
53}
54
55pub struct IncomingCallNotification {
56 state: Arc<IncomingCallNotificationState>,
57}
58impl IncomingCallNotificationState {
59 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
60 Self { call, app_state }
61 }
62
63 fn respond(&self, accept: bool, cx: &mut App) {
64 let active_call = ActiveCall::global(cx);
65 if accept {
66 let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
67 let caller_user_id = self.call.calling_user.id;
68 let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
69 let app_state = self.app_state.clone();
70 let cx: &mut App = cx;
71 cx.spawn(async move |cx| {
72 join.await?;
73 if let Some(project_id) = initial_project_id {
74 cx.update(|cx| {
75 if let Some(app_state) = app_state.upgrade() {
76 workspace::join_in_room_project(
77 project_id,
78 caller_user_id,
79 app_state,
80 cx,
81 )
82 .detach_and_log_err(cx);
83 }
84 });
85 }
86 anyhow::Ok(())
87 })
88 .detach_and_log_err(cx);
89 } else {
90 active_call.update(cx, |active_call, cx| {
91 active_call.decline_incoming(cx).log_err();
92 });
93 }
94 }
95}
96
97impl IncomingCallNotification {
98 pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
99 Self {
100 state: Arc::new(IncomingCallNotificationState::new(call, app_state)),
101 }
102 }
103}
104
105impl Render for IncomingCallNotification {
106 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
107 let ui_font = theme::setup_ui_font(window, cx);
108
109 div().size_full().font(ui_font).child(
110 CollabNotification::new(
111 self.state.call.calling_user.avatar_uri.clone(),
112 Button::new("accept", "Accept").on_click({
113 let state = self.state.clone();
114 move |_, _, cx| state.respond(true, cx)
115 }),
116 Button::new("decline", "Decline").on_click({
117 let state = self.state.clone();
118 move |_, _, cx| state.respond(false, cx)
119 }),
120 )
121 .child(v_flex().overflow_hidden().child(Label::new(format!(
122 "{} is sharing a project in Zed",
123 self.state.call.calling_user.github_login
124 )))),
125 )
126 }
127}