Allow adding write access to guests

This commit is contained in:
Conrad Irwin 2024-01-08 16:52:20 -07:00
parent ca0c06b577
commit 844d161c40
16 changed files with 349 additions and 145 deletions

View file

@ -1,5 +1,6 @@
use crate::tests::TestServer;
use call::ActiveCall;
use editor::Editor;
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
use rpc::proto;
use workspace::Workspace;
@ -13,37 +14,18 @@ async fn test_channel_guests(
let mut server = TestServer::start(executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let channel_id = server
.make_channel("the-channel", None, (&client_a, cx_a), &mut [])
.await;
client_a
.channel_store()
.update(cx_a, |channel_store, cx| {
channel_store.set_channel_visibility(channel_id, proto::ChannelVisibility::Public, cx)
})
.await
.unwrap();
client_a
.fs()
.insert_tree(
"/a",
serde_json::json!({
"a.txt": "a-contents",
}),
)
.await;
let active_call_a = cx_a.read(ActiveCall::global);
let channel_id = server
.make_public_channel("the-channel", &client_a, cx_a)
.await;
// Client A shares a project in the channel
let project_a = client_a.build_test_project(cx_a).await;
active_call_a
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
.await
.unwrap();
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
@ -57,33 +39,16 @@ async fn test_channel_guests(
// b should be following a in the shared project.
// B is a guest,
cx_a.executor().run_until_parked();
// todo!() the test window does not call activation handlers
// correctly yet, so this API does not work.
// let project_b = active_call_b.read_with(cx_b, |call, _| {
// call.location()
// .unwrap()
// .upgrade()
// .expect("should not be weak")
// });
let window_b = cx_b.update(|cx| cx.active_window().unwrap());
let cx_b = &mut VisualTestContext::from_window(window_b, cx_b);
let workspace_b = window_b
.downcast::<Workspace>()
.unwrap()
.root_view(cx_b)
.unwrap();
let project_b = workspace_b.update(cx_b, |workspace, _| workspace.project().clone());
executor.run_until_parked();
let active_call_b = cx_b.read(ActiveCall::global);
let project_b =
active_call_b.read_with(cx_b, |call, _| call.location().unwrap().upgrade().unwrap());
assert_eq!(
project_b.read_with(cx_b, |project, _| project.remote_id()),
Some(project_id),
);
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
assert!(project_b
.update(cx_b, |project, cx| {
let worktree_id = project.worktrees().next().unwrap().read(cx).id();
@ -92,3 +57,60 @@ async fn test_channel_guests(
.await
.is_err())
}
#[gpui::test]
async fn test_channel_guest_promotion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
let mut server = TestServer::start(cx_a.executor()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
let channel_id = server
.make_public_channel("the-channel", &client_a, cx_a)
.await;
let project_a = client_a.build_test_project(cx_a).await;
cx_a.update(|cx| workspace::join_channel(channel_id, client_a.app_state.clone(), None, cx))
.await
.unwrap();
// Client A shares a project in the channel
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
cx_a.run_until_parked();
// Client B joins channel A as a guest
cx_b.update(|cx| workspace::join_channel(channel_id, client_b.app_state.clone(), None, cx))
.await
.unwrap();
cx_a.run_until_parked();
let project_b =
active_call_b.read_with(cx_b, |call, _| call.location().unwrap().upgrade().unwrap());
// client B opens 1.txt
let (workspace, cx_b) = client_b.active_workspace(cx_b);
cx_b.simulate_keystrokes("cmd-p 1 enter");
let editor_b = cx_b.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
active_call_a
.update(cx_a, |call, cx| {
call.room().unwrap().update(cx, |room, cx| {
room.set_participant_role(
client_b.user_id().unwrap(),
proto::ChannelRole::Member,
cx,
)
})
})
.await
.unwrap();
cx_a.run_until_parked();
assert!(project_b.read_with(cx_b, |project, _| !project.is_read_only()));
assert!(!editor_b.update(cx_b, |e, cx| e.read_only(cx)));
}

View file

@ -20,7 +20,11 @@ use node_runtime::FakeNodeRuntime;
use notifications::NotificationStore;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
use rpc::{proto::ChannelRole, RECEIVE_TIMEOUT};
use rpc::{
proto::{self, ChannelRole},
RECEIVE_TIMEOUT,
};
use serde_json::json;
use settings::SettingsStore;
use std::{
cell::{Ref, RefCell, RefMut},
@ -228,12 +232,16 @@ impl TestServer {
Project::init(&client, cx);
client::init(&client, cx);
language::init(cx);
editor::init_settings(cx);
editor::init(cx);
workspace::init(app_state.clone(), cx);
audio::init((), cx);
call::init(client.clone(), user_store.clone(), cx);
channel::init(&client, user_store.clone(), cx);
notifications::init(client.clone(), user_store, cx);
collab_ui::init(&app_state, cx);
file_finder::init(cx);
menu::init();
settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
});
client
@ -351,6 +359,31 @@ impl TestServer {
channel_id
}
pub async fn make_public_channel(
&self,
channel: &str,
client: &TestClient,
cx: &mut TestAppContext,
) -> u64 {
let channel_id = self
.make_channel(channel, None, (client, cx), &mut [])
.await;
client
.channel_store()
.update(cx, |channel_store, cx| {
channel_store.set_channel_visibility(
channel_id,
proto::ChannelVisibility::Public,
cx,
)
})
.await
.unwrap();
channel_id
}
pub async fn make_channel_tree(
&self,
channels: &[(&str, Option<&str>)],
@ -580,6 +613,20 @@ impl TestClient {
(project, worktree.read_with(cx, |tree, _| tree.id()))
}
pub async fn build_test_project(&self, cx: &mut TestAppContext) -> Model<Project> {
self.fs()
.insert_tree(
"/a",
json!({
"1.txt": "one\none\none",
"2.txt": "two\ntwo\ntwo",
"3.txt": "three\nthree\nthree",
}),
)
.await;
self.build_local_project("/a", cx).await.0
}
pub fn build_empty_local_project(&self, cx: &mut TestAppContext) -> Model<Project> {
cx.update(|cx| {
Project::local(
@ -619,6 +666,18 @@ impl TestClient {
) -> (View<Workspace>, &'a mut VisualTestContext) {
cx.add_window_view(|cx| Workspace::new(0, project.clone(), self.app_state.clone(), cx))
}
pub fn active_workspace<'a>(
&'a self,
cx: &'a mut TestAppContext,
) -> (View<Workspace>, &'a mut VisualTestContext) {
let window = cx.update(|cx| cx.active_window().unwrap().downcast::<Workspace>().unwrap());
let view = window.root_view(cx).unwrap();
let cx = Box::new(VisualTestContext::from_window(*window.deref(), cx));
// it might be nice to try and cleanup these at the end of each test.
(view, Box::leak(cx))
}
}
impl Drop for TestClient {