Start work on following in zed2
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
71a1125e88
commit
eff3a72fb5
4 changed files with 245 additions and 167 deletions
|
@ -1165,12 +1165,11 @@ impl CollabPanel {
|
||||||
div().into_any_element()
|
div().into_any_element()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.when(!is_current_user, |this| {
|
.when_some(peer_id, |this, peer_id| {
|
||||||
this.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
|
this.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.workspace.update(cx, |workspace, cx| {
|
this.workspace
|
||||||
// workspace.follow(peer_id, cx)
|
.update(cx, |workspace, cx| workspace.follow(peer_id, cx));
|
||||||
});
|
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2708,6 +2708,7 @@ pub enum ElementId {
|
||||||
Integer(usize),
|
Integer(usize),
|
||||||
Name(SharedString),
|
Name(SharedString),
|
||||||
FocusHandle(FocusId),
|
FocusHandle(FocusId),
|
||||||
|
NamedInteger(SharedString, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementId {
|
impl ElementId {
|
||||||
|
@ -2757,3 +2758,9 @@ impl<'a> From<&'a FocusHandle> for ElementId {
|
||||||
ElementId::FocusHandle(handle.id)
|
ElementId::FocusHandle(handle.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(&'static str, EntityId)> for ElementId {
|
||||||
|
fn from((name, id): (&'static str, EntityId)) -> Self {
|
||||||
|
ElementId::NamedInteger(name.into(), id.as_u64() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
use crate::{AppState, FollowerState, Pane, Workspace};
|
use crate::{AppState, FollowerState, Pane, Workspace};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use call::ActiveCall;
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use db::sqlez::{
|
use db::sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
statement::Statement,
|
statement::Statement,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
point, size, AnyWeakView, Bounds, Div, IntoElement, Model, Pixels, Point, View, ViewContext,
|
point, size, AnyWeakView, Bounds, Div, Entity as _, IntoElement, Model, Pixels, Point, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::prelude::*;
|
use ui::{prelude::*, Button};
|
||||||
|
|
||||||
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
||||||
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
||||||
|
@ -207,19 +208,89 @@ impl Member {
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
match self {
|
match self {
|
||||||
Member::Pane(pane) => {
|
Member::Pane(pane) => {
|
||||||
// todo!()
|
let leader = follower_states.get(pane).and_then(|state| {
|
||||||
// let pane_element = if Some(pane.into()) == zoomed {
|
let room = active_call?.read(cx).room()?.read(cx);
|
||||||
// None
|
room.remote_participant_for_peer_id(state.leader_id)
|
||||||
// } else {
|
});
|
||||||
// Some(pane)
|
|
||||||
// };
|
|
||||||
|
|
||||||
div().size_full().child(pane.clone()).into_any()
|
let mut leader_border = None;
|
||||||
|
let mut leader_status_box = None;
|
||||||
|
if let Some(leader) = &leader {
|
||||||
|
let mut leader_color = cx
|
||||||
|
.theme()
|
||||||
|
.players()
|
||||||
|
.color_for_participant(leader.participant_index.0)
|
||||||
|
.cursor;
|
||||||
|
leader_color.fade_out(0.3);
|
||||||
|
leader_border = Some(leader_color);
|
||||||
|
|
||||||
// Stack::new()
|
leader_status_box = match leader.location {
|
||||||
// .with_child(pane_element.contained().with_border(leader_border))
|
ParticipantLocation::SharedProject {
|
||||||
// .with_children(leader_status_box)
|
project_id: leader_project_id,
|
||||||
// .into_any()
|
} => {
|
||||||
|
if Some(leader_project_id) == project.read(cx).remote_id() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let leader_user = leader.user.clone();
|
||||||
|
let leader_user_id = leader.user.id;
|
||||||
|
Some(
|
||||||
|
Button::new(
|
||||||
|
("leader-status", pane.entity_id()),
|
||||||
|
format!(
|
||||||
|
"Follow {} to their active project",
|
||||||
|
leader_user.github_login,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener(
|
||||||
|
move |this, _, cx| {
|
||||||
|
crate::join_remote_project(
|
||||||
|
leader_project_id,
|
||||||
|
leader_user_id,
|
||||||
|
this.app_state().clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParticipantLocation::UnsharedProject => Some(Button::new(
|
||||||
|
("leader-status", pane.entity_id()),
|
||||||
|
format!(
|
||||||
|
"{} is viewing an unshared Zed project",
|
||||||
|
leader.user.github_login
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
ParticipantLocation::External => Some(Button::new(
|
||||||
|
("leader-status", pane.entity_id()),
|
||||||
|
format!(
|
||||||
|
"{} is viewing a window outside of Zed",
|
||||||
|
leader.user.github_login
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
div()
|
||||||
|
.relative()
|
||||||
|
.size_full()
|
||||||
|
.child(pane.clone())
|
||||||
|
.when_some(leader_border, |this, color| {
|
||||||
|
this.border_2().border_color(color)
|
||||||
|
})
|
||||||
|
.when_some(leader_status_box, |this, status_box| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.w_96()
|
||||||
|
.bottom_3()
|
||||||
|
.right_3()
|
||||||
|
.z_index(1)
|
||||||
|
.child(status_box),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.into_any()
|
||||||
|
|
||||||
// let el = div()
|
// let el = div()
|
||||||
// .flex()
|
// .flex()
|
||||||
|
|
|
@ -2270,60 +2270,60 @@ impl Workspace {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn start_following(
|
fn start_following(
|
||||||
// &mut self,
|
&mut self,
|
||||||
// leader_id: PeerId,
|
leader_id: PeerId,
|
||||||
// cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
// ) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
// let pane = self.active_pane().clone();
|
let pane = self.active_pane().clone();
|
||||||
|
|
||||||
// self.last_leaders_by_pane
|
self.last_leaders_by_pane
|
||||||
// .insert(pane.downgrade(), leader_id);
|
.insert(pane.downgrade(), leader_id);
|
||||||
// self.unfollow(&pane, cx);
|
self.unfollow(&pane, cx);
|
||||||
// self.follower_states.insert(
|
self.follower_states.insert(
|
||||||
// pane.clone(),
|
pane.clone(),
|
||||||
// FollowerState {
|
FollowerState {
|
||||||
// leader_id,
|
leader_id,
|
||||||
// active_view_id: None,
|
active_view_id: None,
|
||||||
// items_by_leader_view_id: Default::default(),
|
items_by_leader_view_id: Default::default(),
|
||||||
// },
|
},
|
||||||
// );
|
);
|
||||||
// cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
// let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
|
let room_id = self.active_call()?.read(cx).room()?.read(cx).id();
|
||||||
// let project_id = self.project.read(cx).remote_id();
|
let project_id = self.project.read(cx).remote_id();
|
||||||
// let request = self.app_state.client.request(proto::Follow {
|
let request = self.app_state.client.request(proto::Follow {
|
||||||
// room_id,
|
room_id,
|
||||||
// project_id,
|
project_id,
|
||||||
// leader_id: Some(leader_id),
|
leader_id: Some(leader_id),
|
||||||
// });
|
});
|
||||||
|
|
||||||
// Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
// let response = request.await?;
|
let response = request.await?;
|
||||||
// this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, _| {
|
||||||
// let state = this
|
let state = this
|
||||||
// .follower_states
|
.follower_states
|
||||||
// .get_mut(&pane)
|
.get_mut(&pane)
|
||||||
// .ok_or_else(|| anyhow!("following interrupted"))?;
|
.ok_or_else(|| anyhow!("following interrupted"))?;
|
||||||
// state.active_view_id = if let Some(active_view_id) = response.active_view_id {
|
state.active_view_id = if let Some(active_view_id) = response.active_view_id {
|
||||||
// Some(ViewId::from_proto(active_view_id)?)
|
Some(ViewId::from_proto(active_view_id)?)
|
||||||
// } else {
|
} else {
|
||||||
// None
|
None
|
||||||
// };
|
};
|
||||||
// Ok::<_, anyhow::Error>(())
|
Ok::<_, anyhow::Error>(())
|
||||||
// })??;
|
})??;
|
||||||
// Self::add_views_from_leader(
|
Self::add_views_from_leader(
|
||||||
// this.clone(),
|
this.clone(),
|
||||||
// leader_id,
|
leader_id,
|
||||||
// vec![pane],
|
vec![pane],
|
||||||
// response.views,
|
response.views,
|
||||||
// &mut cx,
|
&mut cx,
|
||||||
// )
|
)
|
||||||
// .await?;
|
.await?;
|
||||||
// this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?;
|
this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?;
|
||||||
// Ok(())
|
Ok(())
|
||||||
// }))
|
}))
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn follow_next_collaborator(
|
// pub fn follow_next_collaborator(
|
||||||
// &mut self,
|
// &mut self,
|
||||||
|
@ -2362,52 +2362,52 @@ impl Workspace {
|
||||||
// self.follow(leader_id, cx)
|
// self.follow(leader_id, cx)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// pub fn follow(
|
pub fn follow(
|
||||||
// &mut self,
|
&mut self,
|
||||||
// leader_id: PeerId,
|
leader_id: PeerId,
|
||||||
// cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
// ) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
// let room = ActiveCall::global(cx).read(cx).room()?.read(cx);
|
let room = ActiveCall::global(cx).read(cx).room()?.read(cx);
|
||||||
// let project = self.project.read(cx);
|
let project = self.project.read(cx);
|
||||||
|
|
||||||
// let Some(remote_participant) = room.remote_participant_for_peer_id(leader_id) else {
|
let Some(remote_participant) = room.remote_participant_for_peer_id(leader_id) else {
|
||||||
// return None;
|
return None;
|
||||||
// };
|
};
|
||||||
|
|
||||||
// let other_project_id = match remote_participant.location {
|
let other_project_id = match remote_participant.location {
|
||||||
// call::ParticipantLocation::External => None,
|
call::ParticipantLocation::External => None,
|
||||||
// call::ParticipantLocation::UnsharedProject => None,
|
call::ParticipantLocation::UnsharedProject => None,
|
||||||
// call::ParticipantLocation::SharedProject { project_id } => {
|
call::ParticipantLocation::SharedProject { project_id } => {
|
||||||
// if Some(project_id) == project.remote_id() {
|
if Some(project_id) == project.remote_id() {
|
||||||
// None
|
None
|
||||||
// } else {
|
} else {
|
||||||
// Some(project_id)
|
Some(project_id)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
|
|
||||||
// // if they are active in another project, follow there.
|
// if they are active in another project, follow there.
|
||||||
// if let Some(project_id) = other_project_id {
|
if let Some(project_id) = other_project_id {
|
||||||
// let app_state = self.app_state.clone();
|
let app_state = self.app_state.clone();
|
||||||
// return Some(crate::join_remote_project(
|
return Some(crate::join_remote_project(
|
||||||
// project_id,
|
project_id,
|
||||||
// remote_participant.user.id,
|
remote_participant.user.id,
|
||||||
// app_state,
|
app_state,
|
||||||
// cx,
|
cx,
|
||||||
// ));
|
));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // if you're already following, find the right pane and focus it.
|
// if you're already following, find the right pane and focus it.
|
||||||
// for (pane, state) in &self.follower_states {
|
for (pane, state) in &self.follower_states {
|
||||||
// if leader_id == state.leader_id {
|
if leader_id == state.leader_id {
|
||||||
// cx.focus(pane);
|
cx.focus_view(pane);
|
||||||
// return None;
|
return None;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Otherwise, follow.
|
// Otherwise, follow.
|
||||||
// self.start_following(leader_id, cx)
|
self.start_following(leader_id, cx)
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn unfollow(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Option<PeerId> {
|
pub fn unfollow(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Option<PeerId> {
|
||||||
let state = self.follower_states.remove(pane)?;
|
let state = self.follower_states.remove(pane)?;
|
||||||
|
@ -2557,57 +2557,55 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // RPC handlers
|
// RPC handlers
|
||||||
|
|
||||||
fn handle_follow(
|
fn handle_follow(
|
||||||
&mut self,
|
&mut self,
|
||||||
_follower_project_id: Option<u64>,
|
follower_project_id: Option<u64>,
|
||||||
_cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> proto::FollowResponse {
|
) -> proto::FollowResponse {
|
||||||
todo!()
|
let client = &self.app_state.client;
|
||||||
|
let project_id = self.project.read(cx).remote_id();
|
||||||
|
|
||||||
// let client = &self.app_state.client;
|
let active_view_id = self.active_item(cx).and_then(|i| {
|
||||||
// let project_id = self.project.read(cx).remote_id();
|
Some(
|
||||||
|
i.to_followable_item_handle(cx)?
|
||||||
|
.remote_id(client, cx)?
|
||||||
|
.to_proto(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// let active_view_id = self.active_item(cx).and_then(|i| {
|
cx.notify();
|
||||||
// Some(
|
|
||||||
// i.to_followable_item_handle(cx)?
|
|
||||||
// .remote_id(client, cx)?
|
|
||||||
// .to_proto(),
|
|
||||||
// )
|
|
||||||
// });
|
|
||||||
|
|
||||||
// cx.notify();
|
self.last_active_view_id = active_view_id.clone();
|
||||||
|
proto::FollowResponse {
|
||||||
// self.last_active_view_id = active_view_id.clone();
|
active_view_id,
|
||||||
// proto::FollowResponse {
|
views: self
|
||||||
// active_view_id,
|
.panes()
|
||||||
// views: self
|
.iter()
|
||||||
// .panes()
|
.flat_map(|pane| {
|
||||||
// .iter()
|
let leader_id = self.leader_for_pane(pane);
|
||||||
// .flat_map(|pane| {
|
pane.read(cx).items().filter_map({
|
||||||
// let leader_id = self.leader_for_pane(pane);
|
let cx = &cx;
|
||||||
// pane.read(cx).items().filter_map({
|
move |item| {
|
||||||
// let cx = &cx;
|
let item = item.to_followable_item_handle(cx)?;
|
||||||
// move |item| {
|
if (project_id.is_none() || project_id != follower_project_id)
|
||||||
// let item = item.to_followable_item_handle(cx)?;
|
&& item.is_project_item(cx)
|
||||||
// if (project_id.is_none() || project_id != follower_project_id)
|
{
|
||||||
// && item.is_project_item(cx)
|
return None;
|
||||||
// {
|
}
|
||||||
// return None;
|
let id = item.remote_id(client, cx)?.to_proto();
|
||||||
// }
|
let variant = item.to_state_proto(cx)?;
|
||||||
// let id = item.remote_id(client, cx)?.to_proto();
|
Some(proto::View {
|
||||||
// let variant = item.to_state_proto(cx)?;
|
id: Some(id),
|
||||||
// Some(proto::View {
|
leader_id,
|
||||||
// id: Some(id),
|
variant: Some(variant),
|
||||||
// leader_id,
|
})
|
||||||
// variant: Some(variant),
|
}
|
||||||
// })
|
})
|
||||||
// }
|
})
|
||||||
// })
|
.collect(),
|
||||||
// })
|
}
|
||||||
// .collect(),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_update_followers(
|
fn handle_update_followers(
|
||||||
|
@ -2627,6 +2625,8 @@ impl Workspace {
|
||||||
update: proto::UpdateFollowers,
|
update: proto::UpdateFollowers,
|
||||||
cx: &mut AsyncWindowContext,
|
cx: &mut AsyncWindowContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
dbg!("process_leader_update", &update);
|
||||||
|
|
||||||
match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
|
match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
|
||||||
proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
|
proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, _| {
|
||||||
|
@ -3762,15 +3762,15 @@ impl Render for Workspace {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl WorkspaceStore {
|
impl WorkspaceStore {
|
||||||
pub fn new(client: Arc<Client>, _cx: &mut ModelContext<Self>) -> Self {
|
pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
workspaces: Default::default(),
|
workspaces: Default::default(),
|
||||||
followers: Default::default(),
|
followers: Default::default(),
|
||||||
_subscriptions: vec![],
|
_subscriptions: vec![
|
||||||
// client.add_request_handler(cx.weak_model(), Self::handle_follow),
|
client.add_request_handler(cx.weak_model(), Self::handle_follow),
|
||||||
// client.add_message_handler(cx.weak_model(), Self::handle_unfollow),
|
client.add_message_handler(cx.weak_model(), Self::handle_unfollow),
|
||||||
// client.add_message_handler(cx.weak_model(), Self::handle_update_followers),
|
client.add_message_handler(cx.weak_model(), Self::handle_update_followers),
|
||||||
// ],
|
],
|
||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3875,11 +3875,13 @@ impl WorkspaceStore {
|
||||||
this: Model<Self>,
|
this: Model<Self>,
|
||||||
envelope: TypedEnvelope<proto::UpdateFollowers>,
|
envelope: TypedEnvelope<proto::UpdateFollowers>,
|
||||||
_: Arc<Client>,
|
_: Arc<Client>,
|
||||||
mut cx: AsyncWindowContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let leader_id = envelope.original_sender_id()?;
|
let leader_id = envelope.original_sender_id()?;
|
||||||
let update = envelope.payload;
|
let update = envelope.payload;
|
||||||
|
|
||||||
|
dbg!("handle_upate_followers");
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
for workspace in &this.workspaces {
|
for workspace in &this.workspaces {
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
@ -4310,12 +4312,11 @@ pub fn join_remote_project(
|
||||||
Some(collaborator.peer_id)
|
Some(collaborator.peer_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo!("uncomment following")
|
if let Some(follow_peer_id) = follow_peer_id {
|
||||||
// if let Some(follow_peer_id) = follow_peer_id {
|
workspace
|
||||||
// workspace
|
.follow(follow_peer_id, cx)
|
||||||
// .follow(follow_peer_id, cx)
|
.map(|follow| follow.detach_and_log_err(cx));
|
||||||
// .map(|follow| follow.detach_and_log_err(cx));
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue