From f2b62c3946d7392469bebcf0857c893e0e8135bc Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:52:44 +0100 Subject: [PATCH] Start emitting notifications for calls --- Cargo.lock | 1 + crates/collab_ui2/src/collab_ui.rs | 58 +-- crates/collab_ui2/src/notifications.rs | 16 +- .../incoming_call_notification.rs | 346 ++++++++++-------- crates/zed2/Cargo.toml | 2 +- crates/zed2/src/main.rs | 2 +- 6 files changed, 239 insertions(+), 186 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99d4827966c7ef4c1e5427d59d01c144906fb7a2..693f2d9156e4d8ae683b77f445bb06242e2a36c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11644,6 +11644,7 @@ dependencies = [ "async-recursion 0.3.2", "async-tar", "async-trait", + "audio2", "auto_update2", "backtrace", "call2", diff --git a/crates/collab_ui2/src/collab_ui.rs b/crates/collab_ui2/src/collab_ui.rs index d2e6b28115dacf147d1a2ac7dff25e9de9dd8de8..d1e4bea7d62464d7bc10517ac60f632fa80b8a06 100644 --- a/crates/collab_ui2/src/collab_ui.rs +++ b/crates/collab_ui2/src/collab_ui.rs @@ -7,11 +7,14 @@ pub mod notification_panel; pub mod notifications; mod panel_settings; -use std::sync::Arc; +use std::{rc::Rc, sync::Arc}; pub use collab_panel::CollabPanel; pub use collab_titlebar_item::CollabTitlebarItem; -use gpui::AppContext; +use gpui::{ + point, px, AppContext, GlobalPixels, Pixels, PlatformDisplay, Point, Size, WindowBounds, + WindowKind, WindowOptions, +}; pub use panel_settings::{ ChatPanelSettings, CollaborationPanelSettings, NotificationPanelSettings, }; @@ -23,7 +26,7 @@ use workspace::AppState; // [ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall] // ); -pub fn init(_app_state: &Arc, cx: &mut AppContext) { +pub fn init(app_state: &Arc, cx: &mut AppContext) { CollaborationPanelSettings::register(cx); ChatPanelSettings::register(cx); NotificationPanelSettings::register(cx); @@ -32,7 +35,7 @@ pub fn init(_app_state: &Arc, cx: &mut AppContext) { collab_titlebar_item::init(cx); collab_panel::init(cx); // chat_panel::init(cx); - // notifications::init(&app_state, cx); + notifications::init(&app_state, cx); // cx.add_global_action(toggle_screen_sharing); // cx.add_global_action(toggle_mute); @@ -95,31 +98,30 @@ pub fn init(_app_state: &Arc, cx: &mut AppContext) { // } // } -// fn notification_window_options( -// screen: Rc, -// window_size: Vector2F, -// ) -> WindowOptions<'static> { -// const NOTIFICATION_PADDING: f32 = 16.; +fn notification_window_options( + screen: Rc, + window_size: Size, +) -> WindowOptions { + let notification_padding = Pixels::from(16.); -// let screen_bounds = screen.content_bounds(); -// WindowOptions { -// bounds: WindowBounds::Fixed(RectF::new( -// screen_bounds.upper_right() -// + vec2f( -// -NOTIFICATION_PADDING - window_size.x(), -// NOTIFICATION_PADDING, -// ), -// window_size, -// )), -// titlebar: None, -// center: false, -// focus: false, -// show: true, -// kind: WindowKind::PopUp, -// is_movable: false, -// screen: Some(screen), -// } -// } + let screen_bounds = screen.bounds(); + let size: Size = window_size.into(); + + let bounds = gpui::Bounds:: { + origin: screen_bounds.origin, + size: window_size.into(), + }; + WindowOptions { + bounds: WindowBounds::Fixed(bounds), + titlebar: None, + center: false, + focus: false, + show: true, + kind: WindowKind::PopUp, + is_movable: false, + display_id: Some(screen.id()), + } +} // fn render_avatar( // avatar: Option>, diff --git a/crates/collab_ui2/src/notifications.rs b/crates/collab_ui2/src/notifications.rs index bc5d7ad3bf0f80fa1f16e8f60337e8abdbde5018..b58473476acb40b29ecb9754133ce6ce6e8fbf5c 100644 --- a/crates/collab_ui2/src/notifications.rs +++ b/crates/collab_ui2/src/notifications.rs @@ -1,11 +1,11 @@ -// use gpui::AppContext; -// use std::sync::Arc; -// use workspace::AppState; +use gpui::AppContext; +use std::sync::Arc; +use workspace::AppState; -// pub mod incoming_call_notification; +pub mod incoming_call_notification; // pub mod project_shared_notification; -// pub fn init(app_state: &Arc, cx: &mut AppContext) { -// incoming_call_notification::init(app_state, cx); -// project_shared_notification::init(app_state, cx); -// } +pub fn init(app_state: &Arc, cx: &mut AppContext) { + incoming_call_notification::init(app_state, cx); + //project_shared_notification::init(app_state, cx); +} diff --git a/crates/collab_ui2/src/notifications/incoming_call_notification.rs b/crates/collab_ui2/src/notifications/incoming_call_notification.rs index c614a814caf1757b37306cfca5b7d570fc0fac0f..d400b14f5f362c7c60658a2dd857b65785ccc693 100644 --- a/crates/collab_ui2/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui2/src/notifications/incoming_call_notification.rs @@ -3,12 +3,12 @@ use call::{ActiveCall, IncomingCall}; use client::proto; use futures::StreamExt; use gpui::{ - elements::*, - geometry::vector::vec2f, - platform::{CursorStyle, MouseButton}, - AnyElement, AppContext, Entity, View, ViewContext, WindowHandle, + blue, div, green, px, red, AnyElement, AppContext, Component, Context, Div, Element, Entity, + EventEmitter, GlobalPixels, ParentElement, Render, RenderOnce, StatefulInteractiveElement, + Styled, View, ViewContext, VisualContext as _, WindowHandle, }; use std::sync::{Arc, Weak}; +use ui::{h_stack, v_stack, Avatar, Button, Label}; use util::ResultExt; use workspace::AppState; @@ -19,23 +19,44 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { let mut notification_windows: Vec> = Vec::new(); while let Some(incoming_call) = incoming_call.next().await { for window in notification_windows.drain(..) { - window.remove(&mut cx); + window.update(&mut cx, |this, cx| { + //cx.remove_window(); + }); + //cx.update_window(window.into(), |this, cx| cx.remove_window()); + //window.remove(&mut cx); } if let Some(incoming_call) = incoming_call { - let window_size = cx.read(|cx| { - let theme = &theme::current(cx).incoming_call_notification; - vec2f(theme.window_width, theme.window_height) - }); + let unique_screens = cx.update(|cx| cx.displays()).unwrap(); + let window_size = gpui::Size { + width: px(380.), + height: px(64.), + }; - for screen in cx.platform().screens() { + for window in unique_screens { + let options = notification_window_options(window, window_size); + dbg!(&options); let window = cx - .add_window(notification_window_options(screen, window_size), |_| { - IncomingCallNotification::new(incoming_call.clone(), app_state.clone()) - }); - + .open_window(options, |cx| { + cx.build_view(|_| { + IncomingCallNotification::new( + incoming_call.clone(), + app_state.clone(), + ) + }) + }) + .unwrap(); notification_windows.push(window); } + + // for screen in cx.platform().screens() { + // let window = cx + // .add_window(notification_window_options(screen, window_size), |_| { + // IncomingCallNotification::new(incoming_call.clone(), app_state.clone()) + // }); + + // notification_windows.push(window); + // } } } }) @@ -47,167 +68,196 @@ struct RespondToCall { accept: bool, } -pub struct IncomingCallNotification { +struct IncomingCallNotificationState { call: IncomingCall, app_state: Weak, } -impl IncomingCallNotification { +pub struct IncomingCallNotification { + state: Arc, +} +impl IncomingCallNotificationState { pub fn new(call: IncomingCall, app_state: Weak) -> Self { Self { call, app_state } } - fn respond(&mut self, accept: bool, cx: &mut ViewContext) { + fn respond(&self, accept: bool, cx: &mut AppContext) { let active_call = ActiveCall::global(cx); if accept { let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx)); let caller_user_id = self.call.calling_user.id; let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id); let app_state = self.app_state.clone(); - cx.app_context() - .spawn(|mut cx| async move { - join.await?; - if let Some(project_id) = initial_project_id { - cx.update(|cx| { - if let Some(app_state) = app_state.upgrade() { - workspace::join_remote_project( - project_id, - caller_user_id, - app_state, - cx, - ) - .detach_and_log_err(cx); - } - }); - } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + let cx: &mut AppContext = cx; + cx.spawn(|mut cx| async move { + join.await?; + if let Some(project_id) = initial_project_id { + cx.update(|cx| { + if let Some(app_state) = app_state.upgrade() { + // workspace::join_remote_project( + // project_id, + // caller_user_id, + // app_state, + // cx, + // ) + // .detach_and_log_err(cx); + } + }); + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } else { active_call.update(cx, |active_call, cx| { active_call.decline_incoming(cx).log_err(); }); } } +} - fn render_caller(&self, cx: &mut ViewContext) -> AnyElement { - let theme = &theme::current(cx).incoming_call_notification; - let default_project = proto::ParticipantProject::default(); - let initial_project = self - .call - .initial_project - .as_ref() - .unwrap_or(&default_project); - Flex::row() - .with_children(self.call.calling_user.avatar.clone().map(|avatar| { - Image::from_data(avatar) - .with_style(theme.caller_avatar) - .aligned() - })) - .with_child( - Flex::column() - .with_child( - Label::new( - self.call.calling_user.github_login.clone(), - theme.caller_username.text.clone(), - ) - .contained() - .with_style(theme.caller_username.container), - ) - .with_child( - Label::new( - format!( - "is sharing a project in Zed{}", - if initial_project.worktree_root_names.is_empty() { - "" - } else { - ":" - } - ), - theme.caller_message.text.clone(), - ) - .contained() - .with_style(theme.caller_message.container), - ) - .with_children(if initial_project.worktree_root_names.is_empty() { - None - } else { - Some( - Label::new( - initial_project.worktree_root_names.join(", "), - theme.worktree_roots.text.clone(), - ) - .contained() - .with_style(theme.worktree_roots.container), - ) - }) - .contained() - .with_style(theme.caller_metadata) - .aligned(), - ) - .contained() - .with_style(theme.caller_container) - .flex(1., true) - .into_any() +impl IncomingCallNotification { + pub fn new(call: IncomingCall, app_state: Weak) -> Self { + Self { + state: Arc::new(IncomingCallNotificationState::new(call, app_state)), + } } - - fn render_buttons(&self, cx: &mut ViewContext) -> AnyElement { - enum Accept {} - enum Decline {} - - let theme = theme::current(cx); - Flex::column() - .with_child( - MouseEventHandler::new::(0, cx, |_, _| { - let theme = &theme.incoming_call_notification; - Label::new("Accept", theme.accept_button.text.clone()) - .aligned() - .contained() - .with_style(theme.accept_button.container) - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, this, cx| { - this.respond(true, cx); - }) - .flex(1., true), + fn render_caller(&self, cx: &mut ViewContext) -> impl Element { + h_stack() + .children( + self.state + .call + .calling_user + .avatar + .as_ref() + .map(|avatar| Avatar::new(avatar.clone())), ) - .with_child( - MouseEventHandler::new::(0, cx, |_, _| { - let theme = &theme.incoming_call_notification; - Label::new("Decline", theme.decline_button.text.clone()) - .aligned() - .contained() - .with_style(theme.decline_button.container) - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, this, cx| { - this.respond(false, cx); - }) - .flex(1., true), + .child( + v_stack() + .child(Label::new(format!( + "{} is sharing a project in Zed", + self.state.call.calling_user.github_login + ))) + .child(self.render_buttons(cx)), ) - .constrained() - .with_width(theme.incoming_call_notification.button_width) - .into_any() + // let theme = &theme::current(cx).incoming_call_notification; + // let default_project = proto::ParticipantProject::default(); + // let initial_project = self + // .call + // .initial_project + // .as_ref() + // .unwrap_or(&default_project); + // Flex::row() + // .with_children(self.call.calling_user.avatar.clone().map(|avatar| { + // Image::from_data(avatar) + // .with_style(theme.caller_avatar) + // .aligned() + // })) + // .with_child( + // Flex::column() + // .with_child( + // Label::new( + // self.call.calling_user.github_login.clone(), + // theme.caller_username.text.clone(), + // ) + // .contained() + // .with_style(theme.caller_username.container), + // ) + // .with_child( + // Label::new( + // format!( + // "is sharing a project in Zed{}", + // if initial_project.worktree_root_names.is_empty() { + // "" + // } else { + // ":" + // } + // ), + // theme.caller_message.text.clone(), + // ) + // .contained() + // .with_style(theme.caller_message.container), + // ) + // .with_children(if initial_project.worktree_root_names.is_empty() { + // None + // } else { + // Some( + // Label::new( + // initial_project.worktree_root_names.join(", "), + // theme.worktree_roots.text.clone(), + // ) + // .contained() + // .with_style(theme.worktree_roots.container), + // ) + // }) + // .contained() + // .with_style(theme.caller_metadata) + // .aligned(), + // ) + // .contained() + // .with_style(theme.caller_container) + // .flex(1., true) + // .into_any() } -} -impl Entity for IncomingCallNotification { - type Event = (); -} + fn render_buttons(&self, cx: &mut ViewContext) -> impl Element { + h_stack() + .child(Button::new("Accept").render(cx).bg(green()).on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(true, cx) + })) + .child(Button::new("Decline").render(cx).bg(red()).on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(false, cx) + })) -impl View for IncomingCallNotification { - fn ui_name() -> &'static str { - "IncomingCallNotification" - } + // enum Accept {} + // enum Decline {} - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let background = theme::current(cx).incoming_call_notification.background; - Flex::row() - .with_child(self.render_caller(cx)) - .with_child(self.render_buttons(cx)) - .contained() - .with_background_color(background) - .expanded() - .into_any() + // let theme = theme::current(cx); + // Flex::column() + // .with_child( + // MouseEventHandler::new::(0, cx, |_, _| { + // let theme = &theme.incoming_call_notification; + // Label::new("Accept", theme.accept_button.text.clone()) + // .aligned() + // .contained() + // .with_style(theme.accept_button.container) + // }) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_click(MouseButton::Left, |_, this, cx| { + // this.respond(true, cx); + // }) + // .flex(1., true), + // ) + // .with_child( + // MouseEventHandler::new::(0, cx, |_, _| { + // let theme = &theme.incoming_call_notification; + // Label::new("Decline", theme.decline_button.text.clone()) + // .aligned() + // .contained() + // .with_style(theme.decline_button.container) + // }) + // .with_cursor_style(CursorStyle::PointingHand) + // .on_click(MouseButton::Left, |_, this, cx| { + // this.respond(false, cx); + // }) + // .flex(1., true), + // ) + // .constrained() + // .with_width(theme.incoming_call_notification.button_width) + // .into_any() + } +} +impl Render for IncomingCallNotification { + type Element = Div; + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div().bg(red()).flex_none().child(self.render_caller(cx)) + // Flex::row() + // .with_child() + // .with_child(self.render_buttons(cx)) + // .contained() + // .with_background_color(background) + // .expanded() + // .into_any() } } diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 24648f87f1d69587cb8b73d6584159a13c91788e..3212b6182b7b64fe1d79a2f20097fffd64c603b5 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] ai = { package = "ai2", path = "../ai2"} -# audio = { path = "../audio" } +audio = { package = "audio2", path = "../audio2" } # activity_indicator = { path = "../activity_indicator" } auto_update = { package = "auto_update2", path = "../auto_update2" } # breadcrumbs = { path = "../breadcrumbs" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index b18eccace798b701f44bd9925d5eb74b0c3d01c1..c9ed26436ab8e07529c2365d8bb91ec989eed51a 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -199,7 +199,7 @@ fn main() { }); cx.set_global(Arc::downgrade(&app_state)); - // audio::init(Assets, cx); + audio::init(Assets, cx); auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx); workspace::init(app_state.clone(), cx);