incoming_call_notification.rs

  1use crate::notification_window_options;
  2use call::{ActiveCall, IncomingCall};
  3use futures::StreamExt;
  4use gpui::{
  5    img, px, AppContext, ParentElement, Render, RenderOnce, Styled, ViewContext,
  6    VisualContext as _, WindowHandle,
  7};
  8use settings::Settings;
  9use std::sync::{Arc, Weak};
 10use theme::ThemeSettings;
 11use ui::prelude::*;
 12use ui::{h_stack, v_stack, Button, Label};
 13use util::ResultExt;
 14use workspace::AppState;
 15
 16pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
 17    let app_state = Arc::downgrade(app_state);
 18    let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
 19    cx.spawn(|mut cx| async move {
 20        let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
 21        while let Some(incoming_call) = incoming_call.next().await {
 22            for window in notification_windows.drain(..) {
 23                window
 24                    .update(&mut cx, |_, cx| {
 25                        // todo!()
 26                        cx.remove_window();
 27                    })
 28                    .log_err();
 29            }
 30
 31            if let Some(incoming_call) = incoming_call {
 32                let unique_screens = cx.update(|cx| cx.displays()).unwrap();
 33                let window_size = gpui::Size {
 34                    width: px(380.),
 35                    height: px(64.),
 36                };
 37
 38                for screen in unique_screens {
 39                    let options = notification_window_options(screen, window_size);
 40                    let window = cx
 41                        .open_window(options, |cx| {
 42                            cx.new_view(|_| {
 43                                IncomingCallNotification::new(
 44                                    incoming_call.clone(),
 45                                    app_state.clone(),
 46                                )
 47                            })
 48                        })
 49                        .unwrap();
 50                    notification_windows.push(window);
 51                }
 52            }
 53        }
 54    })
 55    .detach();
 56}
 57
 58#[derive(Clone, PartialEq)]
 59struct RespondToCall {
 60    accept: bool,
 61}
 62
 63struct IncomingCallNotificationState {
 64    call: IncomingCall,
 65    app_state: Weak<AppState>,
 66}
 67
 68pub struct IncomingCallNotification {
 69    state: Arc<IncomingCallNotificationState>,
 70}
 71impl IncomingCallNotificationState {
 72    pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
 73        Self { call, app_state }
 74    }
 75
 76    fn respond(&self, accept: bool, cx: &mut AppContext) {
 77        let active_call = ActiveCall::global(cx);
 78        if accept {
 79            let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
 80            let caller_user_id = self.call.calling_user.id;
 81            let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
 82            let app_state = self.app_state.clone();
 83            let cx: &mut AppContext = cx;
 84            cx.spawn(|cx| async move {
 85                join.await?;
 86                if let Some(project_id) = initial_project_id {
 87                    cx.update(|cx| {
 88                        if let Some(app_state) = app_state.upgrade() {
 89                            workspace::join_remote_project(
 90                                project_id,
 91                                caller_user_id,
 92                                app_state,
 93                                cx,
 94                            )
 95                            .detach_and_log_err(cx);
 96                        }
 97                    })
 98                    .log_err();
 99                }
100                anyhow::Ok(())
101            })
102            .detach_and_log_err(cx);
103        } else {
104            active_call.update(cx, |active_call, cx| {
105                active_call.decline_incoming(cx).log_err();
106            });
107        }
108    }
109}
110
111impl IncomingCallNotification {
112    pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
113        Self {
114            state: Arc::new(IncomingCallNotificationState::new(call, app_state)),
115        }
116    }
117}
118
119impl Render for IncomingCallNotification {
120    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
121        // TODO: Is there a better place for us to initialize the font?
122        let (ui_font, ui_font_size) = {
123            let theme_settings = ThemeSettings::get_global(cx);
124            (
125                theme_settings.ui_font.family.clone(),
126                theme_settings.ui_font_size.clone(),
127            )
128        };
129
130        cx.set_rem_size(ui_font_size);
131
132        h_stack()
133            .font(ui_font)
134            .text_ui()
135            .justify_between()
136            .size_full()
137            .overflow_hidden()
138            .elevation_3(cx)
139            .p_2()
140            .gap_2()
141            .child(
142                img(self.state.call.calling_user.avatar_uri.clone())
143                    .w_12()
144                    .h_12()
145                    .rounded_full(),
146            )
147            .child(v_stack().overflow_hidden().child(Label::new(format!(
148                "{} is sharing a project in Zed",
149                self.state.call.calling_user.github_login
150            ))))
151            .child(
152                v_stack()
153                    .child(Button::new("accept", "Accept").render(cx).on_click({
154                        let state = self.state.clone();
155                        move |_, cx| state.respond(true, cx)
156                    }))
157                    .child(Button::new("decline", "Decline").render(cx).on_click({
158                        let state = self.state.clone();
159                        move |_, cx| state.respond(false, cx)
160                    })),
161            )
162    }
163}