incoming_call_notification.rs

  1use crate::notification_window_options;
  2use call::{ActiveCall, IncomingCall};
  3use futures::StreamExt;
  4use gpui::{
  5    div, px, red, AppContext, Div, Element, ParentElement, Render, RenderOnce, Styled, ViewContext,
  6    VisualContext as _, WindowHandle,
  7};
  8use std::sync::{Arc, Weak};
  9use ui::prelude::*;
 10use ui::{h_stack, v_stack, Avatar, Button, Label};
 11use util::ResultExt;
 12use workspace::AppState;
 13
 14pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
 15    let app_state = Arc::downgrade(app_state);
 16    let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
 17    cx.spawn(|mut cx| async move {
 18        let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
 19        while let Some(incoming_call) = incoming_call.next().await {
 20            for window in notification_windows.drain(..) {
 21                window
 22                    .update(&mut cx, |_, cx| {
 23                        // todo!()
 24                        cx.remove_window();
 25                    })
 26                    .log_err();
 27            }
 28
 29            if let Some(incoming_call) = incoming_call {
 30                let unique_screens = cx.update(|cx| cx.displays()).unwrap();
 31                let window_size = gpui::Size {
 32                    width: px(380.),
 33                    height: px(64.),
 34                };
 35
 36                for window in unique_screens {
 37                    let options = notification_window_options(window, window_size);
 38                    let window = cx
 39                        .open_window(options, |cx| {
 40                            cx.build_view(|_| {
 41                                IncomingCallNotification::new(
 42                                    incoming_call.clone(),
 43                                    app_state.clone(),
 44                                )
 45                            })
 46                        })
 47                        .unwrap();
 48                    notification_windows.push(window);
 49                }
 50
 51                // for screen in cx.platform().screens() {
 52                //     let window = cx
 53                //         .add_window(notification_window_options(screen, window_size), |_| {
 54                //             IncomingCallNotification::new(incoming_call.clone(), app_state.clone())
 55                //         });
 56
 57                //     notification_windows.push(window);
 58                // }
 59            }
 60        }
 61    })
 62    .detach();
 63}
 64
 65#[derive(Clone, PartialEq)]
 66struct RespondToCall {
 67    accept: bool,
 68}
 69
 70struct IncomingCallNotificationState {
 71    call: IncomingCall,
 72    app_state: Weak<AppState>,
 73}
 74
 75pub struct IncomingCallNotification {
 76    state: Arc<IncomingCallNotificationState>,
 77}
 78impl IncomingCallNotificationState {
 79    pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
 80        Self { call, app_state }
 81    }
 82
 83    fn respond(&self, accept: bool, cx: &mut AppContext) {
 84        let active_call = ActiveCall::global(cx);
 85        if accept {
 86            let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx));
 87            let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id);
 88            let app_state = self.app_state.clone();
 89            let cx: &mut AppContext = cx;
 90            cx.spawn(|cx| async move {
 91                join.await?;
 92                if let Some(_project_id) = initial_project_id {
 93                    cx.update(|_cx| {
 94                        if let Some(_app_state) = app_state.upgrade() {
 95                            // workspace::join_remote_project(
 96                            //     project_id,
 97                            //     caller_user_id,
 98                            //     app_state,
 99                            //     cx,
100                            // )
101                            // .detach_and_log_err(cx);
102                        }
103                    })
104                    .log_err();
105                }
106                anyhow::Ok(())
107            })
108            .detach_and_log_err(cx);
109        } else {
110            active_call.update(cx, |active_call, cx| {
111                active_call.decline_incoming(cx).log_err();
112            });
113        }
114    }
115}
116
117impl IncomingCallNotification {
118    pub fn new(call: IncomingCall, app_state: Weak<AppState>) -> Self {
119        Self {
120            state: Arc::new(IncomingCallNotificationState::new(call, app_state)),
121        }
122    }
123    fn render_caller(&self, cx: &mut ViewContext<Self>) -> impl Element {
124        h_stack()
125            .children(
126                self.state
127                    .call
128                    .calling_user
129                    .avatar
130                    .as_ref()
131                    .map(|avatar| Avatar::data(avatar.clone())),
132            )
133            .child(
134                v_stack()
135                    .child(Label::new(format!(
136                        "{} is sharing a project in Zed",
137                        self.state.call.calling_user.github_login
138                    )))
139                    .child(self.render_buttons(cx)),
140            )
141        // let theme = &theme::current(cx).incoming_call_notification;
142        // let default_project = proto::ParticipantProject::default();
143        // let initial_project = self
144        //     .call
145        //     .initial_project
146        //     .as_ref()
147        //     .unwrap_or(&default_project);
148        // Flex::row()
149        //     .with_children(self.call.calling_user.avatar.clone().map(|avatar| {
150        //         Image::from_data(avatar)
151        //             .with_style(theme.caller_avatar)
152        //             .aligned()
153        //     }))
154        //     .with_child(
155        //         Flex::column()
156        //             .with_child(
157        //                 Label::new(
158        //                     self.call.calling_user.github_login.clone(),
159        //                     theme.caller_username.text.clone(),
160        //                 )
161        //                 .contained()
162        //                 .with_style(theme.caller_username.container),
163        //             )
164        //             .with_child(
165        //                 Label::new(
166        //                     format!(
167        //                         "is sharing a project in Zed{}",
168        //                         if initial_project.worktree_root_names.is_empty() {
169        //                             ""
170        //                         } else {
171        //                             ":"
172        //                         }
173        //                     ),
174        //                     theme.caller_message.text.clone(),
175        //                 )
176        //                 .contained()
177        //                 .with_style(theme.caller_message.container),
178        //             )
179        //             .with_children(if initial_project.worktree_root_names.is_empty() {
180        //                 None
181        //             } else {
182        //                 Some(
183        //                     Label::new(
184        //                         initial_project.worktree_root_names.join(", "),
185        //                         theme.worktree_roots.text.clone(),
186        //                     )
187        //                     .contained()
188        //                     .with_style(theme.worktree_roots.container),
189        //                 )
190        //             })
191        //             .contained()
192        //             .with_style(theme.caller_metadata)
193        //             .aligned(),
194        //     )
195        //     .contained()
196        //     .with_style(theme.caller_container)
197        //     .flex(1., true)
198        //     .into_any()
199    }
200
201    fn render_buttons(&self, cx: &mut ViewContext<Self>) -> impl Element {
202        h_stack()
203            .child(
204                Button::new("accept", "Accept")
205                    .render(cx)
206                    // .bg(green())
207                    .on_click({
208                        let state = self.state.clone();
209                        move |_, cx| state.respond(true, cx)
210                    }),
211            )
212            .child(
213                Button::new("decline", "Decline")
214                    .render(cx)
215                    // .bg(red())
216                    .on_click({
217                        let state = self.state.clone();
218                        move |_, cx| state.respond(false, cx)
219                    }),
220            )
221
222        // enum Accept {}
223        // enum Decline {}
224
225        // let theme = theme::current(cx);
226        // Flex::column()
227        //     .with_child(
228        //         MouseEventHandler::new::<Accept, _>(0, cx, |_, _| {
229        //             let theme = &theme.incoming_call_notification;
230        //             Label::new("Accept", theme.accept_button.text.clone())
231        //                 .aligned()
232        //                 .contained()
233        //                 .with_style(theme.accept_button.container)
234        //         })
235        //         .with_cursor_style(CursorStyle::PointingHand)
236        //         .on_click(MouseButton::Left, |_, this, cx| {
237        //             this.respond(true, cx);
238        //         })
239        //         .flex(1., true),
240        //     )
241        //     .with_child(
242        //         MouseEventHandler::new::<Decline, _>(0, cx, |_, _| {
243        //             let theme = &theme.incoming_call_notification;
244        //             Label::new("Decline", theme.decline_button.text.clone())
245        //                 .aligned()
246        //                 .contained()
247        //                 .with_style(theme.decline_button.container)
248        //         })
249        //         .with_cursor_style(CursorStyle::PointingHand)
250        //         .on_click(MouseButton::Left, |_, this, cx| {
251        //             this.respond(false, cx);
252        //         })
253        //         .flex(1., true),
254        //     )
255        //     .constrained()
256        //     .with_width(theme.incoming_call_notification.button_width)
257        //     .into_any()
258    }
259}
260impl Render for IncomingCallNotification {
261    type Element = Div;
262    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
263        div().bg(red()).flex_none().child(self.render_caller(cx))
264        // Flex::row()
265        //     .with_child()
266        //     .with_child(self.render_buttons(cx))
267        //     .contained()
268        //     .with_background_color(background)
269        //     .expanded()
270        //     .into_any()
271    }
272}