From 383c21046f1c1e924cae5db33650b0fbe5b0b24e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 14:27:59 +0200 Subject: [PATCH] Set room location when active workspace changes --- Cargo.lock | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 41 +++++++++-- crates/theme/src/theme.rs | 1 + crates/workspace/Cargo.toml | 9 ++- crates/workspace/src/pane_group.rs | 74 ++++++++++++++++---- crates/workspace/src/workspace.rs | 9 ++- styles/src/styleTree/workspace.ts | 4 ++ 7 files changed, 119 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0211f4d1e1..2e70000673 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7084,6 +7084,7 @@ name = "workspace" version = "0.1.0" dependencies = [ "anyhow", + "call", "client", "collections", "context_menu", diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 8e4b8f9f32..cea3654856 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -76,6 +76,10 @@ impl CollabTitlebarItem { let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify())); subscriptions.push(cx.observe(&active_call, |_, _, cx| cx.notify())); + subscriptions.push(cx.observe_window_activation(|this, active, cx| { + this.window_activation_changed(active, cx) + })); + Self { workspace: workspace.downgrade(), contacts_popover: None, @@ -83,14 +87,43 @@ impl CollabTitlebarItem { } } + fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { + let workspace = self.workspace.upgrade(cx); + let room = ActiveCall::global(cx).read(cx).room().cloned(); + if let Some((workspace, room)) = workspace.zip(room) { + let workspace = workspace.read(cx); + let project = if !active { + None + } else if workspace.project().read(cx).remote_id().is_some() { + Some(workspace.project().clone()) + } else { + None + }; + + room.update(cx, |room, cx| { + room.set_location(project.as_ref(), cx) + .detach_and_log_err(cx); + }); + } + } + fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { if let Some(workspace) = self.workspace.upgrade(cx) { - if let Some(room) = ActiveCall::global(cx).read(cx).room() { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let window_id = cx.window_id(); let room_id = room.read(cx).id(); let project = workspace.read(cx).project().clone(); - project - .update(cx, |project, cx| project.share(room_id, cx)) - .detach_and_log_err(cx); + let share = project.update(cx, |project, cx| project.share(room_id, cx)); + cx.spawn_weak(|_, mut cx| async move { + share.await?; + if cx.update(|cx| cx.window_is_active(window_id)) { + room.update(&mut cx, |room, cx| { + room.set_location(Some(&project), cx).detach_and_log_err(cx); + }); + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 6253b6dabb..bc7e6f0995 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -58,6 +58,7 @@ pub struct Workspace { pub notifications: Notifications, pub joining_project_avatar: ImageStyle, pub joining_project_message: ContainedText, + pub external_location_message: ContainedText, pub dock: Dock, } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 759bff2cbd..2fd43b7bcb 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -8,9 +8,15 @@ path = "src/workspace.rs" doctest = false [features] -test-support = ["client/test-support", "project/test-support", "settings/test-support"] +test-support = [ + "call/test-support", + "client/test-support", + "project/test-support", + "settings/test-support" +] [dependencies] +call = { path = "../call" } client = { path = "../client" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } @@ -32,6 +38,7 @@ serde_json = { version = "1.0", features = ["preserve_order"] } smallvec = { version = "1.6", features = ["union"] } [dev-dependencies] +call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 94acf427e4..fb04c4ead6 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,8 +1,9 @@ use crate::{FollowerStatesByLeader, Pane}; use anyhow::{anyhow, Result}; +use call::ActiveCall; use client::PeerId; use collections::HashMap; -use gpui::{elements::*, Axis, Border, ViewHandle}; +use gpui::{elements::*, AppContext, Axis, Border, ViewHandle}; use project::Collaborator; use serde::Deserialize; use theme::Theme; @@ -56,11 +57,14 @@ impl PaneGroup { pub(crate) fn render( &self, + project_id: Option, theme: &Theme, follower_states: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { - self.root.render(theme, follower_states, collaborators) + self.root + .render(project_id, theme, follower_states, collaborators, cx) } pub(crate) fn panes(&self) -> Vec<&ViewHandle> { @@ -100,13 +104,14 @@ impl Member { pub fn render( &self, + project_id: Option, theme: &Theme, follower_states: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { match self { Member::Pane(pane) => { - let mut border = Border::default(); let leader = follower_states .iter() .find_map(|(leader_id, follower_states)| { @@ -116,21 +121,61 @@ impl Member { None } }) - .and_then(|leader_id| collaborators.get(leader_id)); - if let Some(leader) = leader { - let leader_color = theme - .editor - .replica_selection_style(leader.replica_id) - .cursor; - border = Border::all(theme.workspace.leader_border_width, leader_color); + .and_then(|leader_id| { + let room = ActiveCall::global(cx).read(cx).room()?.read(cx); + let collaborator = collaborators.get(leader_id)?; + let participant = room.remote_participants().get(&leader_id)?; + Some((collaborator.replica_id, participant)) + }); + + if let Some((replica_id, leader)) = leader { + let view = match leader.location { + call::ParticipantLocation::Project { + project_id: leader_project_id, + } => { + if Some(leader_project_id) == project_id { + ChildView::new(pane).boxed() + } else { + Label::new( + format!( + "Follow {} on their currently active project", + leader.user.github_login, + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .boxed() + } + } + call::ParticipantLocation::External => Label::new( + format!( + "{} is viewing a window outside of Zed", + leader.user.github_login + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .boxed(), + }; + + let leader_color = theme.editor.replica_selection_style(replica_id).cursor; + let mut border = Border::all(theme.workspace.leader_border_width, leader_color); border .color .fade_out(1. - theme.workspace.leader_border_opacity); border.overlay = true; + Container::new(view).with_border(border).boxed() + } else { + ChildView::new(pane).boxed() } - ChildView::new(pane).contained().with_border(border).boxed() } - Member::Axis(axis) => axis.render(theme, follower_states, collaborators), + Member::Axis(axis) => { + axis.render(project_id, theme, follower_states, collaborators, cx) + } } } @@ -232,14 +277,17 @@ impl PaneAxis { fn render( &self, + project_id: Option, theme: &Theme, follower_state: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { let last_member_ix = self.members.len() - 1; Flex::new(self.axis) .with_children(self.members.iter().enumerate().map(|(ix, member)| { - let mut member = member.render(theme, follower_state, collaborators); + let mut member = + member.render(project_id, theme, follower_state, collaborators, cx); if ix < last_member_ix { let mut border = theme.workspace.pane_divider; border.left = false; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7aa93f47d9..5bec4b4c6d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -12,7 +12,8 @@ mod status_bar; mod toolbar; use anyhow::{anyhow, Context, Result}; -use client::{proto, Client, Contact, PeerId, Subscription, TypedEnvelope, UserStore}; +use call::ActiveCall; +use client::{proto, Client, Contact, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; @@ -860,7 +861,7 @@ pub struct Workspace { weak_self: WeakViewHandle, client: Arc, user_store: ModelHandle, - remote_entity_subscription: Option, + remote_entity_subscription: Option, fs: Arc, modal: Option, center: PaneGroup, @@ -880,6 +881,7 @@ pub struct Workspace { last_leaders_by_pane: HashMap, PeerId>, window_edited: bool, _observe_current_user: Task<()>, + _active_call_observation: gpui::Subscription, } #[derive(Default)] @@ -1015,6 +1017,7 @@ impl Workspace { last_leaders_by_pane: Default::default(), window_edited: false, _observe_current_user, + _active_call_observation: cx.observe(&ActiveCall::global(cx), |_, _, cx| cx.notify()), }; this.project_remote_id_changed(this.project.read(cx).remote_id(), cx); cx.defer(|this, cx| this.update_window_title(cx)); @@ -2430,9 +2433,11 @@ impl View for Workspace { Flex::column() .with_child( FlexItem::new(self.center.render( + self.project.read(cx).remote_id(), &theme, &self.follower_states_by_leader, self.project.read(cx).collaborators(), + cx, )) .flex(1., true) .boxed(), diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 40ed44e8db..0877f131c1 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -48,6 +48,10 @@ export default function workspace(theme: Theme) { padding: 12, ...text(theme, "sans", "primary", { size: "lg" }), }, + externalLocationMessage: { + padding: 12, + ...text(theme, "sans", "primary", { size: "lg" }), + }, leaderBorderOpacity: 0.7, leaderBorderWidth: 2.0, tabBar: tabBar(theme),