diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index dc15dfbae8..c621c47156 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -6880,6 +6880,7 @@ impl Render for ProjectPanel { // version that understands these messages. let is_collab = project.is_via_collab(); let is_local = project.is_local(); + let supports_undo = cx.has_flag::(); if has_worktree { let item_count = self @@ -7011,13 +7012,6 @@ impl Render for ProjectPanel { .on_action(cx.listener(Self::fold_directory)) .on_action(cx.listener(Self::remove_from_project)) .on_action(cx.listener(Self::compare_marked_files)) - .when( - !is_collab && cx.has_flag::(), - |el| { - el.on_action(cx.listener(Self::undo)) - .on_action(cx.listener(Self::redo)) - }, - ) .when(!project.is_read_only(cx), |el| { el.on_action(cx.listener(Self::new_file)) .on_action(cx.listener(Self::new_directory)) @@ -7031,6 +7025,10 @@ impl Render for ProjectPanel { .on_action(cx.listener(Self::add_to_gitignore)) .on_action(cx.listener(Self::add_to_git_info_exclude)) .when(!is_collab, |el| el.on_action(cx.listener(Self::trash))) + .when(!is_collab && supports_undo, |el| { + el.on_action(cx.listener(Self::undo)) + .on_action(cx.listener(Self::redo)) + }) }) .when( project.is_local() || project.is_via_wsl_with_host_interop(cx), diff --git a/crates/project_panel/src/tests/undo.rs b/crates/project_panel/src/tests/undo.rs index 774a71b6d0..9f607d9c07 100644 --- a/crates/project_panel/src/tests/undo.rs +++ b/crates/project_panel/src/tests/undo.rs @@ -1,5 +1,6 @@ #![cfg(test)] +use client::proto; use collections::HashSet; use fs::{FakeFs, Fs}; use gpui::{Entity, VisualTestContext}; @@ -240,6 +241,12 @@ impl TestContext { .unwrap(); let mut cx = VisualTestContext::from_window(window.into(), cx); let panel = workspace.update_in(&mut cx, ProjectPanel::new); + + workspace.update_in(&mut cx, |workspace, window, cx| { + workspace.add_panel(panel.clone(), window, cx); + workspace.focus_panel::(window, cx); + }); + cx.run_until_parked(); TestContext { panel, fs, cx } @@ -458,3 +465,37 @@ async fn record_via_collab(cx: &mut gpui::TestAppContext) { cx.redo().await; cx.assert_fs_state_is(&["b.txt", "renamed.txt"]); } + +#[gpui::test] +async fn undo_redo_unavailable_for_read_only_collab_guest(cx: &mut gpui::TestAppContext) { + let mut cx = TestContext::new(cx).await; + let focus_handle = cx + .panel + .read_with(&cx.cx, |panel, _| panel.focus_handle.clone()); + + cx.cx.update(|window, _cx| { + assert!(window.is_action_available_in(&crate::Undo, &focus_handle)); + assert!(window.is_action_available_in(&crate::Redo, &focus_handle)); + }); + + // In order to simulate a read-only project, we mark it both as a collab + // session as well as being a guest, which only has read access. + // This is currently a bit redundant, seeing as these actions are already + // disabled in collab either way. However, we'll want to enable undo/redo in + // collab in the future and this test will ensure that, at that point, we + // continue to not allow undo/redo in read-only projects. + cx.panel.update(&mut cx.cx, |panel, cx| { + panel.project.update(cx, |project, cx| { + project.mark_as_collab_for_testing(); + project.set_role(proto::ChannelRole::Guest, cx); + }); + + assert!(panel.project.read(cx).is_read_only(cx)); + cx.notify(); + }); + + cx.cx.update(|window, _cx| { + assert!(!window.is_action_available_in(&crate::Undo, &focus_handle)); + assert!(!window.is_action_available_in(&crate::Redo, &focus_handle)); + }); +}