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