Remove JoinProject internal action

This commit is contained in:
Antonio Scandurra 2023-04-28 11:12:09 +02:00
parent f881f9e3d8
commit d953729233
12 changed files with 302 additions and 287 deletions

View file

@ -25,7 +25,6 @@ use client::{
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockDefaultItemFactory, ToggleDockButton};
use drag_and_drop::DragAndDrop;
use fs::{self, Fs};
use futures::{
channel::{mpsc, oneshot},
future::try_join_all,
@ -135,12 +134,6 @@ pub struct OpenPaths {
#[derive(Clone, Deserialize, PartialEq)]
pub struct ActivatePane(pub usize);
#[derive(Clone, PartialEq)]
pub struct JoinProject {
pub project_id: u64,
pub follow_user_id: u64,
}
#[derive(Clone, PartialEq)]
pub struct OpenSharedScreen {
pub peer_id: PeerId,
@ -216,7 +209,6 @@ pub type WorkspaceId = i64;
impl_internal_actions!(
workspace,
[
JoinProject,
OpenSharedScreen,
RemoveWorktreeFromProject,
SplitWithItem,
@ -524,10 +516,7 @@ pub enum Event {
pub struct Workspace {
weak_self: WeakViewHandle<Self>,
client: Arc<Client>,
user_store: ModelHandle<client::UserStore>,
remote_entity_subscription: Option<client::Subscription>,
fs: Arc<dyn Fs>,
modal: Option<AnyViewHandle>,
center: PaneGroup,
left_sidebar: ViewHandle<Sidebar>,
@ -548,7 +537,7 @@ pub struct Workspace {
active_call: Option<(ModelHandle<ActiveCall>, Vec<gpui::Subscription>)>,
leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
database_id: WorkspaceId,
background_actions: BackgroundActions,
app_state: Arc<AppState>,
_window_subscriptions: [Subscription; 3],
_apply_leader_updates: Task<Result<()>>,
_observe_current_user: Task<Result<()>>,
@ -578,8 +567,7 @@ impl Workspace {
serialized_workspace: Option<SerializedWorkspace>,
workspace_id: WorkspaceId,
project: ModelHandle<Project>,
dock_default_factory: DockDefaultItemFactory,
background_actions: BackgroundActions,
app_state: Arc<AppState>,
cx: &mut ViewContext<Self>,
) -> Self {
cx.observe(&project, |_, _, cx| cx.notify()).detach();
@ -616,8 +604,8 @@ impl Workspace {
let weak_handle = cx.weak_handle();
let center_pane =
cx.add_view(|cx| Pane::new(weak_handle.clone(), None, background_actions, cx));
let center_pane = cx
.add_view(|cx| Pane::new(weak_handle.clone(), None, app_state.background_actions, cx));
let pane_id = center_pane.id();
cx.subscribe(&center_pane, move |this, _, event, cx| {
this.handle_pane_event(pane_id, event, cx)
@ -625,14 +613,15 @@ impl Workspace {
.detach();
cx.focus(&center_pane);
cx.emit(Event::PaneAdded(center_pane.clone()));
let dock = Dock::new(dock_default_factory, background_actions, cx);
let dock = Dock::new(
app_state.dock_default_item_factory,
app_state.background_actions,
cx,
);
let dock_pane = dock.pane().clone();
let fs = project.read(cx).fs().clone();
let user_store = project.read(cx).user_store();
let client = project.read(cx).client();
let mut current_user = user_store.read(cx).watch_current_user();
let mut connection_status = client.status();
let mut current_user = app_state.user_store.read(cx).watch_current_user();
let mut connection_status = app_state.client.status();
let _observe_current_user = cx.spawn(|this, mut cx| async move {
current_user.recv().await;
connection_status.recv().await;
@ -725,10 +714,7 @@ impl Workspace {
status_bar,
titlebar_item: None,
notifications: Default::default(),
client,
remote_entity_subscription: None,
user_store,
fs,
left_sidebar,
right_sidebar,
project: project.clone(),
@ -738,7 +724,7 @@ impl Workspace {
window_edited: false,
active_call,
database_id: workspace_id,
background_actions,
app_state,
_observe_current_user,
_apply_leader_updates,
leader_updates_tx,
@ -827,8 +813,7 @@ impl Workspace {
serialized_workspace,
workspace_id,
project_handle.clone(),
app_state.dock_default_item_factory,
app_state.background_actions,
app_state.clone(),
cx,
);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
@ -938,8 +923,12 @@ impl Workspace {
&self.status_bar
}
pub fn app_state(&self) -> &Arc<AppState> {
&self.app_state
}
pub fn user_store(&self) -> &ModelHandle<UserStore> {
&self.user_store
&self.app_state.user_store
}
pub fn project(&self) -> &ModelHandle<Project> {
@ -947,7 +936,7 @@ impl Workspace {
}
pub fn client(&self) -> &Client {
&self.client
&self.app_state.client
}
pub fn set_titlebar_item(&mut self, item: AnyViewHandle, cx: &mut ViewContext<Self>) {
@ -1183,7 +1172,7 @@ impl Workspace {
visible: bool,
cx: &mut ViewContext<Self>,
) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>> {
let fs = self.fs.clone();
let fs = self.app_state.fs.clone();
// Sort the paths to ensure we add worktrees for parents before their children.
abs_paths.sort_unstable();
@ -1494,8 +1483,14 @@ impl Workspace {
}
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
let pane =
cx.add_view(|cx| Pane::new(self.weak_handle(), None, self.background_actions, cx));
let pane = cx.add_view(|cx| {
Pane::new(
self.weak_handle(),
None,
self.app_state.background_actions,
cx,
)
});
let pane_id = pane.id();
cx.subscribe(&pane, move |this, _, event, cx| {
this.handle_pane_event(pane_id, event, cx)
@ -1688,7 +1683,7 @@ impl Workspace {
proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView {
id: self.active_item(cx).and_then(|item| {
item.to_followable_item_handle(cx)?
.remote_id(&self.client, cx)
.remote_id(&self.app_state.client, cx)
.map(|id| id.to_proto())
}),
leader_id: self.leader_for_pane(&pane),
@ -1857,8 +1852,11 @@ impl Workspace {
fn project_remote_id_changed(&mut self, remote_id: Option<u64>, cx: &mut ViewContext<Self>) {
if let Some(remote_id) = remote_id {
self.remote_entity_subscription =
Some(self.client.add_view_for_remote_entity(remote_id, cx));
self.remote_entity_subscription = Some(
self.app_state
.client
.add_view_for_remote_entity(remote_id, cx),
);
} else {
self.remote_entity_subscription.take();
}
@ -1898,7 +1896,7 @@ impl Workspace {
cx.notify();
let project_id = self.project.read(cx).remote_id()?;
let request = self.client.request(proto::Follow {
let request = self.app_state.client.request(proto::Follow {
project_id,
leader_id: Some(leader_id),
});
@ -1977,7 +1975,8 @@ impl Workspace {
if states_by_pane.is_empty() {
self.follower_states_by_leader.remove(&leader_id);
if let Some(project_id) = self.project.read(cx).remote_id() {
self.client
self.app_state
.client
.send(proto::Unfollow {
project_id,
leader_id: Some(leader_id),
@ -2158,7 +2157,7 @@ impl Workspace {
mut cx: AsyncAppContext,
) -> Result<proto::FollowResponse> {
this.update(&mut cx, |this, cx| {
let client = &this.client;
let client = &this.app_state.client;
this.leader_state
.followers
.insert(envelope.original_sender_id()?);
@ -2366,7 +2365,8 @@ impl Workspace {
) -> Option<()> {
let project_id = self.project.read(cx).remote_id()?;
if !self.leader_state.followers.is_empty() {
self.client
self.app_state
.client
.send(proto::UpdateFollowers {
project_id,
follower_ids: self.leader_state.followers.iter().copied().collect(),
@ -2698,7 +2698,18 @@ impl Workspace {
#[cfg(any(test, feature = "test-support"))]
pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
Self::new(None, 0, project, |_, _| None, || &[], cx)
let app_state = Arc::new(AppState {
languages: project.read(cx).languages().clone(),
themes: ThemeRegistry::new((), cx.font_cache().clone()),
client: project.read(cx).client(),
user_store: project.read(cx).user_store(),
fs: project.read(cx).fs().clone(),
build_window_options: |_, _, _| Default::default(),
initialize_workspace: |_, _, _| {},
dock_default_item_factory: |_, _| None,
background_actions: || &[],
});
Self::new(None, 0, project, app_state, cx)
}
}
@ -2784,6 +2795,7 @@ impl View for Workspace {
&self.follower_states_by_leader,
self.active_call(),
self.active_pane(),
&self.app_state,
cx,
))
.flex(1., true),
@ -3015,6 +3027,87 @@ pub fn open_new(
})
}
pub fn join_remote_project(
project_id: u64,
follow_user_id: u64,
app_state: Arc<AppState>,
cx: &mut AppContext,
) -> Task<Result<()>> {
cx.spawn(|mut cx| async move {
let existing_workspace = cx.update(|cx| {
cx.window_ids()
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
.find(|workspace| {
workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
})
});
let workspace = if let Some(existing_workspace) = existing_workspace {
existing_workspace.downgrade()
} 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.clone(), cx);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
workspace
},
);
workspace.downgrade()
};
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_being_followed(follow_peer_id) {
workspace
.toggle_follow(follow_peer_id, cx)
.map(|follow| follow.detach_and_log_err(cx));
}
}
}
})?;
anyhow::Ok(())
})
}
fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
let mut parts = value.split(',');
let width: usize = parts.next()?.parse().ok()?;
@ -3041,16 +3134,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| {
Workspace::new(
Default::default(),
0,
project.clone(),
|_, _| None,
|| &[],
cx,
)
});
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
// Adding an item with no ambiguity renders the tab without detail.
let item1 = cx.add_view(&workspace, |_| {
@ -3114,16 +3198,7 @@ mod tests {
.await;
let project = Project::test(fs, ["root1".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| {
Workspace::new(
Default::default(),
0,
project.clone(),
|_, _| None,
|| &[],
cx,
)
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let worktree_id = project.read_with(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
});
@ -3213,16 +3288,7 @@ mod tests {
fs.insert_tree("/root", json!({ "one": "" })).await;
let project = Project::test(fs, ["root".as_ref()], cx).await;
let (window_id, workspace) = cx.add_window(|cx| {
Workspace::new(
Default::default(),
0,
project.clone(),
|_, _| None,
|| &[],
cx,
)
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
// When there are no dirty items, there's nothing to do.
let item1 = cx.add_view(&workspace, |_| TestItem::new());
@ -3257,9 +3323,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, None, cx).await;
let (window_id, workspace) = cx.add_window(|cx| {
Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx)
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
let item1 = cx.add_view(&workspace, |cx| {
TestItem::new()
@ -3366,9 +3430,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| {
Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx)
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
// Create several workspace items with single project entries, and two
// workspace items with multiple project entries.
@ -3475,9 +3537,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| {
Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx)
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
let item = cx.add_view(&workspace, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
@ -3594,9 +3654,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (_, workspace) = cx.add_window(|cx| {
Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx)
});
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
let item = cx.add_view(&workspace, |cx| {
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])