diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index c34d124162..596a0ec853 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -1,4 +1,3 @@ -mod indicator; pub mod participant; pub mod room; @@ -10,22 +9,17 @@ use collections::HashSet; use postage::watch; use gpui::{ - actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, - Subscription, Task, ViewHandle, WeakModelHandle, + AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, + Subscription, Task, WeakModelHandle, }; use project::Project; -use settings::Settings; -use indicator::SharingStatusIndicator; pub use participant::ParticipantLocation; pub use room::Room; -actions!(collab, [ToggleScreenSharing]); - pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx)); cx.set_global(active_call); - cx.add_global_action(toggle_screen_sharing); } #[derive(Clone)] @@ -36,6 +30,7 @@ pub struct IncomingCall { pub initial_project: Option, } +/// Singleton global maintaining the user's participation in a room across workspaces. pub struct ActiveCall { room: Option<(ModelHandle, Vec)>, location: Option>, @@ -46,7 +41,6 @@ pub struct ActiveCall { ), client: Arc, user_store: ModelHandle, - sharing_status_indicator: Option<(usize, ViewHandle)>, _subscriptions: Vec, } @@ -71,7 +65,6 @@ impl ActiveCall { ], client, user_store, - sharing_status_indicator: None, } } @@ -290,8 +283,6 @@ impl ActiveCall { this.set_room(None, cx).detach_and_log_err(cx); } - this.set_sharing_status(room.read(cx).is_screen_sharing(), cx); - cx.notify(); }), cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), @@ -316,30 +307,4 @@ impl ActiveCall { pub fn pending_invites(&self) -> &HashSet { &self.pending_invites } - - pub fn set_sharing_status(&mut self, is_screen_sharing: bool, cx: &mut MutableAppContext) { - if is_screen_sharing { - if self.sharing_status_indicator.is_none() - && cx.global::().show_call_status_icon - { - self.sharing_status_indicator = - Some(cx.add_status_bar_item(|_| SharingStatusIndicator)); - } - } else if let Some((window_id, _)) = self.sharing_status_indicator.take() { - cx.remove_status_bar_item(window_id); - } - } -} - -pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext) { - if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let toggle_screen_sharing = room.update(cx, |room, cx| { - if room.is_screen_sharing() { - Task::ready(room.unshare_screen(cx)) - } else { - room.share_screen(cx) - } - }); - toggle_screen_sharing.detach_and_log_err(cx); - } } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 116a331578..9f2c0fbee9 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,5 +1,5 @@ -use crate::{contact_notification::ContactNotification, contacts_popover}; -use call::{ActiveCall, ParticipantLocation, ToggleScreenSharing}; +use crate::{contact_notification::ContactNotification, contacts_popover, ToggleScreenSharing}; +use call::{ActiveCall, ParticipantLocation}; use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore}; use clock::ReplicaId; use contacts_popover::ContactsPopover; diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 38a47e87dc..d26e2c99cc 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -6,14 +6,17 @@ mod contacts_popover; mod incoming_call_notification; mod notifications; mod project_shared_notification; +mod sharing_status_indicator; use anyhow::anyhow; use call::ActiveCall; pub use collab_titlebar_item::{CollabTitlebarItem, ToggleCollaborationMenu}; -use gpui::MutableAppContext; +use gpui::{actions, MutableAppContext, Task}; use std::sync::Arc; use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; +actions!(collab, [ToggleScreenSharing]); + pub fn init(app_state: Arc, cx: &mut MutableAppContext) { collab_titlebar_item::init(cx); contact_notification::init(cx); @@ -22,89 +25,107 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); incoming_call_notification::init(cx); project_shared_notification::init(cx); + sharing_status_indicator::init(cx); + cx.add_global_action(toggle_screen_sharing); cx.add_global_action(move |action: &JoinProject, cx| { - let project_id = action.project_id; - let follow_user_id = action.follow_user_id; - let app_state = app_state.clone(); - cx.spawn(|mut cx| async move { - let existing_workspace = cx.update(|cx| { - cx.window_ids() - .filter_map(|window_id| cx.root_view::(window_id)) - .find(|workspace| { - workspace.read(cx).project().read(cx).remote_id() == Some(project_id) - }) - }); - - let workspace = if let Some(existing_workspace) = existing_workspace { - existing_workspace - } else { - let active_call = cx.read(ActiveCall::global); - let room = active_call - .read_with(&cx, |call, _| call.room().cloned()) - .ok_or_else(|| anyhow!("not in a call"))?; - let project = room - .update(&mut cx, |room, cx| { - room.join_project( - project_id, - app_state.languages.clone(), - app_state.fs.clone(), - cx, - ) - }) - .await?; - - let (_, workspace) = cx.add_window( - (app_state.build_window_options)(None, None, cx.platform().as_ref()), - |cx| { - let mut workspace = Workspace::new( - Default::default(), - 0, - project, - app_state.dock_default_item_factory, - cx, - ); - (app_state.initialize_workspace)(&mut workspace, &app_state, cx); - workspace - }, - ); - workspace - }; - - cx.activate_window(workspace.window_id()); - cx.platform().activate(true); - - workspace.update(&mut cx, |workspace, cx| { - if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let follow_peer_id = room - .read(cx) - .remote_participants() - .iter() - .find(|(_, participant)| participant.user.id == follow_user_id) - .map(|(_, p)| p.peer_id) - .or_else(|| { - // If we couldn't follow the given user, follow the host instead. - let collaborator = workspace - .project() - .read(cx) - .collaborators() - .values() - .find(|collaborator| collaborator.replica_id == 0)?; - Some(collaborator.peer_id) - }); - - if let Some(follow_peer_id) = follow_peer_id { - if !workspace.is_following(follow_peer_id) { - workspace - .toggle_follow(&ToggleFollow(follow_peer_id), cx) - .map(|follow| follow.detach_and_log_err(cx)); - } - } - } - }); - - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + join_project(action, app_state.clone(), cx); }); } + +pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let toggle_screen_sharing = room.update(cx, |room, cx| { + if room.is_screen_sharing() { + Task::ready(room.unshare_screen(cx)) + } else { + room.share_screen(cx) + } + }); + toggle_screen_sharing.detach_and_log_err(cx); + } +} + +fn join_project(action: &JoinProject, app_state: Arc, cx: &mut MutableAppContext) { + let project_id = action.project_id; + let follow_user_id = action.follow_user_id; + cx.spawn(|mut cx| async move { + let existing_workspace = cx.update(|cx| { + cx.window_ids() + .filter_map(|window_id| cx.root_view::(window_id)) + .find(|workspace| { + workspace.read(cx).project().read(cx).remote_id() == Some(project_id) + }) + }); + + let workspace = if let Some(existing_workspace) = existing_workspace { + existing_workspace + } else { + let active_call = cx.read(ActiveCall::global); + let room = active_call + .read_with(&cx, |call, _| call.room().cloned()) + .ok_or_else(|| anyhow!("not in a call"))?; + let project = room + .update(&mut cx, |room, cx| { + room.join_project( + project_id, + app_state.languages.clone(), + app_state.fs.clone(), + cx, + ) + }) + .await?; + + let (_, workspace) = cx.add_window( + (app_state.build_window_options)(None, None, cx.platform().as_ref()), + |cx| { + let mut workspace = Workspace::new( + Default::default(), + 0, + project, + app_state.dock_default_item_factory, + cx, + ); + (app_state.initialize_workspace)(&mut workspace, &app_state, cx); + workspace + }, + ); + workspace + }; + + cx.activate_window(workspace.window_id()); + cx.platform().activate(true); + + workspace.update(&mut cx, |workspace, cx| { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let follow_peer_id = room + .read(cx) + .remote_participants() + .iter() + .find(|(_, participant)| participant.user.id == follow_user_id) + .map(|(_, p)| p.peer_id) + .or_else(|| { + // If we couldn't follow the given user, follow the host instead. + let collaborator = workspace + .project() + .read(cx) + .collaborators() + .values() + .find(|collaborator| collaborator.replica_id == 0)?; + Some(collaborator.peer_id) + }); + + if let Some(follow_peer_id) = follow_peer_id { + if !workspace.is_following(follow_peer_id) { + workspace + .toggle_follow(&ToggleFollow(follow_peer_id), cx) + .map(|follow| follow.detach_and_log_err(cx)); + } + } + } + }); + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); +} diff --git a/crates/call/src/indicator.rs b/crates/collab_ui/src/sharing_status_indicator.rs similarity index 55% rename from crates/call/src/indicator.rs rename to crates/collab_ui/src/sharing_status_indicator.rs index 102ea5c551..541194ec66 100644 --- a/crates/call/src/indicator.rs +++ b/crates/collab_ui/src/sharing_status_indicator.rs @@ -1,11 +1,31 @@ +use call::ActiveCall; use gpui::{ color::Color, elements::{MouseEventHandler, Svg}, - Appearance, Element, ElementBox, Entity, MouseButton, RenderContext, View, + Appearance, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View, }; +use settings::Settings; use crate::ToggleScreenSharing; +pub fn init(cx: &mut MutableAppContext) { + let active_call = ActiveCall::global(cx); + + let mut status_indicator = None; + cx.observe(&active_call, move |call, cx| { + if let Some(room) = call.read(cx).room() { + if room.read(cx).is_screen_sharing() { + if status_indicator.is_none() && cx.global::().show_call_status_icon { + status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator)); + } + } else if let Some((window_id, _)) = status_indicator.take() { + cx.remove_status_bar_item(window_id); + } + } + }) + .detach(); +} + pub struct SharingStatusIndicator; impl Entity for SharingStatusIndicator {