Serialize initial follow state in leader and reflect it in follower

This commit is contained in:
Antonio Scandurra 2022-03-18 10:22:13 +01:00
parent 0fdaa1d715
commit f0b7bd6e17
10 changed files with 246 additions and 63 deletions

View file

@ -109,7 +109,7 @@ pub struct Pane {
pub(crate) struct FollowerState {
pub(crate) leader_id: PeerId,
pub(crate) current_view_id: usize,
pub(crate) current_view_id: Option<usize>,
pub(crate) items_by_leader_view_id: HashMap<usize, Box<dyn ItemHandle>>,
}
@ -308,6 +308,11 @@ impl Pane {
}
pub(crate) fn add_item(&mut self, mut item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
// Prevent adding the same item to the pane more than once.
if self.items.iter().any(|i| i.id() == item.id()) {
return;
}
item.set_nav_history(self.nav_history.clone(), cx);
item.added_to_pane(cx);
let item_idx = cmp::min(self.active_item_index + 1, self.items.len());
@ -321,13 +326,14 @@ impl Pane {
follower_state: FollowerState,
cx: &mut ViewContext<Self>,
) -> Result<()> {
let current_view_id = follower_state.current_view_id as usize;
let item = follower_state
.items_by_leader_view_id
.get(&current_view_id)
.ok_or_else(|| anyhow!("invalid current view id"))?
.clone();
self.add_item(item, cx);
if let Some(current_view_id) = follower_state.current_view_id {
let item = follower_state
.items_by_leader_view_id
.get(&current_view_id)
.ok_or_else(|| anyhow!("invalid current view id"))?
.clone();
self.add_item(item, cx);
}
Ok(())
}
@ -335,6 +341,12 @@ impl Pane {
self.items.iter()
}
pub fn items_of_type<'a, T: View>(&'a self) -> impl 'a + Iterator<Item = ViewHandle<T>> {
self.items
.iter()
.filter_map(|item| item.to_any().downcast())
}
pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
self.items.get(self.active_item_index).cloned()
}

View file

@ -111,8 +111,8 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
),
]);
client.add_entity_request_handler(Workspace::handle_follow);
client.add_model_message_handler(Workspace::handle_unfollow);
client.add_view_request_handler(Workspace::handle_follow);
client.add_view_message_handler(Workspace::handle_unfollow);
}
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
@ -235,7 +235,7 @@ pub trait FollowedItem {
where
Self: Sized;
fn to_state_message(&self, cx: &mut MutableAppContext) -> proto::view::Variant;
fn to_state_message(&self, cx: &AppContext) -> proto::view::Variant;
}
pub trait ItemHandle: 'static {
@ -262,6 +262,8 @@ pub trait ItemHandle: 'static {
cx: &mut MutableAppContext,
) -> Task<Result<()>>;
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle>;
fn can_be_followed(&self, cx: &AppContext) -> bool;
fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant>;
}
pub trait WeakItemHandle {
@ -297,11 +299,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
Box::new(self.clone())
}
fn clone_on_split(
&self,
// nav_history: Rc<RefCell<NavHistory>>,
cx: &mut MutableAppContext,
) -> Option<Box<dyn ItemHandle>> {
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemHandle>> {
self.update(cx, |item, cx| {
cx.add_option_view(|cx| item.clone_on_split(cx))
})
@ -381,6 +379,16 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyViewHandle> {
self.read(cx).act_as_type(type_id, self, cx)
}
fn can_be_followed(&self, cx: &AppContext) -> bool {
self.read(cx).as_followed().is_some()
}
fn to_state_message(&self, cx: &AppContext) -> Option<proto::view::Variant> {
self.read(cx)
.as_followed()
.map(|item| item.to_state_message(cx))
}
}
impl Into<AnyViewHandle> for Box<dyn ItemHandle> {
@ -709,6 +717,13 @@ impl Workspace {
}
}
pub fn items<'a>(
&'a self,
cx: &'a AppContext,
) -> impl 'a + Iterator<Item = &Box<dyn ItemHandle>> {
self.panes.iter().flat_map(|pane| pane.read(cx).items())
}
pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
self.items_of_type(cx).max_by_key(|item| item.id())
}
@ -717,11 +732,9 @@ impl Workspace {
&'a self,
cx: &'a AppContext,
) -> impl 'a + Iterator<Item = ViewHandle<T>> {
self.panes.iter().flat_map(|pane| {
pane.read(cx)
.items()
.filter_map(|item| item.to_any().downcast())
})
self.panes
.iter()
.flat_map(|pane| pane.read(cx).items_of_type())
}
pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
@ -1085,7 +1098,7 @@ impl Workspace {
pane.set_follow_state(
FollowerState {
leader_id,
current_view_id: response.current_view_id as usize,
current_view_id: response.current_view_id.map(|id| id as usize),
items_by_leader_view_id,
},
cx,
@ -1310,13 +1323,33 @@ impl Workspace {
async fn handle_follow(
this: ViewHandle<Self>,
envelope: TypedEnvelope<proto::Follow>,
_: TypedEnvelope<proto::Follow>,
_: Arc<Client>,
cx: AsyncAppContext,
) -> Result<proto::FollowResponse> {
Ok(proto::FollowResponse {
current_view_id: 0,
views: Default::default(),
this.read_with(&cx, |this, cx| {
let current_view_id = if let Some(active_item) = this.active_item(cx) {
if active_item.can_be_followed(cx) {
Some(active_item.id() as u64)
} else {
None
}
} else {
None
};
Ok(proto::FollowResponse {
current_view_id,
views: this
.items(cx)
.filter_map(|item| {
let variant = item.to_state_message(cx)?;
Some(proto::View {
id: item.id() as u64,
variant: Some(variant),
})
})
.collect(),
})
})
}