Allow active call to be optional on workspace

Antonio Scandurra created

This prepares us for a future where the workspace is unaware of the
active call and doesn't require all tests to invoke `call::init`.

Change summary

crates/workspace/src/pane_group.rs | 12 ++++--
crates/workspace/src/workspace.rs  | 53 +++++++++++++++++++------------
crates/zed/src/zed.rs              |  1 
3 files changed, 42 insertions(+), 24 deletions(-)

Detailed changes

crates/workspace/src/pane_group.rs 🔗

@@ -60,9 +60,11 @@ impl PaneGroup {
         project: &ModelHandle<Project>,
         theme: &Theme,
         follower_states: &FollowerStatesByLeader,
+        active_call: Option<&ModelHandle<ActiveCall>>,
         cx: &mut RenderContext<Workspace>,
     ) -> ElementBox {
-        self.root.render(project, theme, follower_states, cx)
+        self.root
+            .render(project, theme, follower_states, active_call, cx)
     }
 
     pub(crate) fn panes(&self) -> Vec<&ViewHandle<Pane>> {
@@ -105,6 +107,7 @@ impl Member {
         project: &ModelHandle<Project>,
         theme: &Theme,
         follower_states: &FollowerStatesByLeader,
+        active_call: Option<&ModelHandle<ActiveCall>>,
         cx: &mut RenderContext<Workspace>,
     ) -> ElementBox {
         enum FollowIntoExternalProject {}
@@ -121,7 +124,7 @@ impl Member {
                         }
                     })
                     .and_then(|leader_id| {
-                        let room = ActiveCall::global(cx).read(cx).room()?.read(cx);
+                        let room = active_call?.read(cx).room()?.read(cx);
                         let collaborator = project.read(cx).collaborators().get(leader_id)?;
                         let participant = room.remote_participants().get(&leader_id)?;
                         Some((collaborator.replica_id, participant))
@@ -223,7 +226,7 @@ impl Member {
                     .with_children(prompt)
                     .boxed()
             }
-            Member::Axis(axis) => axis.render(project, theme, follower_states, cx),
+            Member::Axis(axis) => axis.render(project, theme, follower_states, active_call, cx),
         }
     }
 
@@ -328,12 +331,13 @@ impl PaneAxis {
         project: &ModelHandle<Project>,
         theme: &Theme,
         follower_state: &FollowerStatesByLeader,
+        active_call: Option<&ModelHandle<ActiveCall>>,
         cx: &mut RenderContext<Workspace>,
     ) -> 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(project, theme, follower_state, cx);
+                let mut member = member.render(project, theme, follower_state, active_call, cx);
                 if ix < last_member_ix {
                     let mut border = theme.workspace.pane_divider;
                     border.left = false;

crates/workspace/src/workspace.rs 🔗

@@ -980,8 +980,9 @@ pub struct Workspace {
     follower_states_by_leader: FollowerStatesByLeader,
     last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
     window_edited: bool,
+    active_call: Option<ModelHandle<ActiveCall>>,
     _observe_current_user: Task<()>,
-    _active_call_observation: gpui::Subscription,
+    _active_call_observation: Option<gpui::Subscription>,
 }
 
 #[derive(Default)]
@@ -1090,6 +1091,14 @@ impl Workspace {
             drag_and_drop.register_container(weak_handle.clone());
         });
 
+        let mut active_call = None;
+        let mut active_call_observation = None;
+        if cx.has_global::<ModelHandle<ActiveCall>>() {
+            let call = cx.global::<ModelHandle<ActiveCall>>().clone();
+            active_call_observation = Some(cx.observe(&call, |_, _, cx| cx.notify()));
+            active_call = Some(call);
+        }
+
         let mut this = Workspace {
             modal: None,
             weak_self: weak_handle,
@@ -1116,8 +1125,9 @@ impl Workspace {
             follower_states_by_leader: Default::default(),
             last_leaders_by_pane: Default::default(),
             window_edited: false,
+            active_call,
             _observe_current_user,
-            _active_call_observation: cx.observe(&ActiveCall::global(cx), |_, _, cx| cx.notify()),
+            _active_call_observation: active_call_observation,
         };
         this.project_remote_id_changed(this.project.read(cx).remote_id(), cx);
         cx.defer(|this, cx| this.update_window_title(cx));
@@ -1248,30 +1258,32 @@ impl Workspace {
         quitting: bool,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<bool>> {
-        let active_call = ActiveCall::global(cx);
+        let active_call = self.active_call.clone();
         let window_id = cx.window_id();
         let workspace_count = cx
             .window_ids()
             .flat_map(|window_id| cx.root_view::<Workspace>(window_id))
             .count();
         cx.spawn(|this, mut cx| async move {
-            if !quitting
-                && workspace_count == 1
-                && active_call.read_with(&cx, |call, _| call.room().is_some())
-            {
-                let answer = cx
-                    .prompt(
-                        window_id,
-                        PromptLevel::Warning,
-                        "Do you want to leave the current call?",
-                        &["Close window and hang up", "Cancel"],
-                    )
-                    .next()
-                    .await;
-                if answer == Some(1) {
-                    return anyhow::Ok(false);
-                } else {
-                    active_call.update(&mut cx, |call, cx| call.hang_up(cx))?;
+            if let Some(active_call) = active_call {
+                if !quitting
+                    && workspace_count == 1
+                    && active_call.read_with(&cx, |call, _| call.room().is_some())
+                {
+                    let answer = cx
+                        .prompt(
+                            window_id,
+                            PromptLevel::Warning,
+                            "Do you want to leave the current call?",
+                            &["Close window and hang up", "Cancel"],
+                        )
+                        .next()
+                        .await;
+                    if answer == Some(1) {
+                        return anyhow::Ok(false);
+                    } else {
+                        active_call.update(&mut cx, |call, cx| call.hang_up(cx))?;
+                    }
                 }
             }
 
@@ -2571,6 +2583,7 @@ impl View for Workspace {
                                                         &project,
                                                         &theme,
                                                         &self.follower_states_by_leader,
+                                                        self.active_call.as_ref(),
                                                         cx,
                                                     ))
                                                     .flex(1., true)

crates/zed/src/zed.rs 🔗

@@ -1755,6 +1755,7 @@ mod tests {
             let state = Arc::get_mut(&mut app_state).unwrap();
             state.initialize_workspace = initialize_workspace;
             state.build_window_options = build_window_options;
+            call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
             workspace::init(app_state.clone(), cx);
             editor::init(cx);
             pane::init(cx);