incoming_call_notification.rs

  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}