diff --git a/crates/client/src/call.rs b/crates/client/src/call.rs index 3111a04949..9b7ce06df4 100644 --- a/crates/client/src/call.rs +++ b/crates/client/src/call.rs @@ -4,6 +4,6 @@ use std::sync::Arc; #[derive(Clone)] pub struct Call { pub room_id: u64, - pub from: Arc, + pub caller: Arc, pub participants: Vec>, } diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 0dbb8bb198..d285cb09db 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -212,7 +212,7 @@ impl UserStore { this.get_users(envelope.payload.participant_user_ids, cx) }) .await?, - from: this + caller: this .update(&mut cx, |this, cx| { this.get_user(envelope.payload.caller_user_id, cx) }) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index d3f12fdf6f..b101bb991d 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,86 +1,14 @@ mod collab_titlebar_item; mod contacts_popover; +mod incoming_call_notification; -use client::{call::Call, UserStore}; +use client::{Client, UserStore}; pub use collab_titlebar_item::CollabTitlebarItem; -use futures::StreamExt; -use gpui::{ - elements::*, - geometry::{rect::RectF, vector::vec2f}, - Entity, ModelHandle, MutableAppContext, View, WindowBounds, WindowKind, WindowOptions, -}; -use settings::Settings; +use gpui::{ModelHandle, MutableAppContext}; +use std::sync::Arc; -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - - let mut incoming_call = user_store.read(cx).incoming_call(); - cx.spawn(|mut cx| async move { - let mut notification_window = None; - while let Some(incoming_call) = incoming_call.next().await { - if let Some(window_id) = notification_window.take() { - cx.remove_window(window_id); - } - - if let Some(incoming_call) = incoming_call { - let (window_id, _) = cx.add_window( - WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), - titlebar: None, - center: true, - kind: WindowKind::PopUp, - is_movable: false, - }, - |_| IncomingCallNotification::new(incoming_call), - ); - notification_window = Some(window_id); - } - } - }) - .detach(); -} - -struct IncomingCallNotification { - call: Call, -} - -impl IncomingCallNotification { - fn new(call: Call) -> Self { - Self { call } - } -} - -impl Entity for IncomingCallNotification { - type Event = (); -} - -impl View for IncomingCallNotification { - fn ui_name() -> &'static str { - "IncomingCallNotification" - } - - fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - let theme = &cx.global::().theme.contacts_panel; - Flex::row() - .with_children(self.call.from.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - self.call.from.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .boxed() - } + incoming_call_notification::init(client, user_store, cx); } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 2ea0c75623..15a987d6c2 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -9,7 +9,7 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; -use room::{ActiveCall, Room}; +use room::ActiveCall; use settings::Settings; use theme::IconButton; diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs new file mode 100644 index 0000000000..ec959f01ea --- /dev/null +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -0,0 +1,152 @@ +use std::sync::Arc; + +use client::{call::Call, Client, UserStore}; +use futures::StreamExt; +use gpui::{ + elements::*, + geometry::{rect::RectF, vector::vec2f}, + impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, + View, ViewContext, WindowBounds, WindowKind, WindowOptions, +}; +use room::ActiveCall; +use settings::Settings; +use util::ResultExt; + +impl_internal_actions!(incoming_call_notification, [RespondToCall]); + +pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { + cx.add_action(IncomingCallNotification::respond_to_call); + + let mut incoming_call = user_store.read(cx).incoming_call(); + cx.spawn(|mut cx| async move { + let mut notification_window = None; + while let Some(incoming_call) = incoming_call.next().await { + if let Some(window_id) = notification_window.take() { + cx.remove_window(window_id); + } + + if let Some(incoming_call) = incoming_call { + let (window_id, _) = cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| { + IncomingCallNotification::new( + incoming_call, + client.clone(), + user_store.clone(), + ) + }, + ); + notification_window = Some(window_id); + } + } + }) + .detach(); +} + +#[derive(Clone, PartialEq)] +struct RespondToCall { + accept: bool, +} + +pub struct IncomingCallNotification { + call: Call, + client: Arc, + user_store: ModelHandle, +} + +impl IncomingCallNotification { + pub fn new(call: Call, client: Arc, user_store: ModelHandle) -> Self { + Self { + call, + client, + user_store, + } + } + + fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { + if action.accept { + ActiveCall::global(cx) + .update(cx, |active_call, cx| { + active_call.join(&self.call, &self.client, &self.user_store, cx) + }) + .detach_and_log_err(cx); + } else { + self.user_store + .update(cx, |user_store, _| user_store.decline_call().log_err()); + } + + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { + let theme = &cx.global::().theme.contacts_panel; + Flex::row() + .with_children( + self.call + .caller + .avatar + .clone() + .map(|avatar| Image::new(avatar).with_style(theme.contact_avatar).boxed()), + ) + .with_child( + Label::new( + self.call.caller.github_login.clone(), + theme.contact_username.text.clone(), + ) + .boxed(), + ) + .boxed() + } + + fn render_buttons(&self, cx: &mut RenderContext) -> ElementBox { + enum Accept {} + enum Decline {} + + Flex::row() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.contacts_panel; + Label::new("Accept".to_string(), theme.contact_username.text.clone()).boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(RespondToCall { accept: true }); + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.contacts_panel; + Label::new("Decline".to_string(), theme.contact_username.text.clone()).boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(RespondToCall { accept: false }); + }) + .boxed(), + ) + .boxed() + } +} + +impl Entity for IncomingCallNotification { + type Event = (); +} + +impl View for IncomingCallNotification { + fn ui_name() -> &'static str { + "IncomingCallNotification" + } + + fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + Flex::column() + .with_child(self.render_caller(cx)) + .with_child(self.render_buttons(cx)) + .boxed() + } +} diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index de769a6e5e..29ad1c5eb0 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,7 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(user_store.clone(), cx); + collab_ui::init(client.clone(), user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx);