diff --git a/crates/collab_ui2/src/channel_view.rs b/crates/collab_ui2/src/channel_view.rs index d2ffc0de57d865d6c8eb047ff85bad007074e4f3..8d2c037f9d3f5807e7d5375df6accb0b676377d6 100644 --- a/crates/collab_ui2/src/channel_view.rs +++ b/crates/collab_ui2/src/channel_view.rs @@ -1,454 +1,444 @@ -// use anyhow::{anyhow, Result}; -// use call::report_call_event_for_channel; -// use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore}; -// use client::{ -// proto::{self, PeerId}, -// Collaborator, ParticipantIndex, -// }; -// use collections::HashMap; -// use editor::{CollaborationHub, Editor}; -// use gpui::{ -// actions, -// elements::{ChildView, Label}, -// geometry::vector::Vector2F, -// AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Subscription, Task, View, -// ViewContext, ViewHandle, -// }; -// use project::Project; -// use smallvec::SmallVec; -// use std::{ -// any::{Any, TypeId}, -// sync::Arc, -// }; -// use util::ResultExt; -// use workspace::{ -// item::{FollowableItem, Item, ItemEvent, ItemHandle}, -// register_followable_item, -// searchable::SearchableItemHandle, -// ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId, -// }; - -// actions!(channel_view, [Deploy]); - -// pub fn init(cx: &mut AppContext) { -// register_followable_item::(cx) -// } - -// pub struct ChannelView { -// pub editor: ViewHandle, -// project: ModelHandle, -// channel_store: ModelHandle, -// channel_buffer: ModelHandle, -// remote_id: Option, -// _editor_event_subscription: Subscription, -// } - -// impl ChannelView { -// pub fn open( -// channel_id: ChannelId, -// workspace: ViewHandle, -// cx: &mut AppContext, -// ) -> Task>> { -// let pane = workspace.read(cx).active_pane().clone(); -// let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx); -// cx.spawn(|mut cx| async move { -// let channel_view = channel_view.await?; -// pane.update(&mut cx, |pane, cx| { -// report_call_event_for_channel( -// "open channel notes", -// channel_id, -// &workspace.read(cx).app_state().client, -// cx, -// ); -// pane.add_item(Box::new(channel_view.clone()), true, true, None, cx); -// }); -// anyhow::Ok(channel_view) -// }) -// } - -// pub fn open_in_pane( -// channel_id: ChannelId, -// pane: ViewHandle, -// workspace: ViewHandle, -// cx: &mut AppContext, -// ) -> Task>> { -// let workspace = workspace.read(cx); -// let project = workspace.project().to_owned(); -// let channel_store = ChannelStore::global(cx); -// let language_registry = workspace.app_state().languages.clone(); -// let markdown = language_registry.language_for_name("Markdown"); -// let channel_buffer = -// channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx)); - -// cx.spawn(|mut cx| async move { -// let channel_buffer = channel_buffer.await?; -// let markdown = markdown.await.log_err(); - -// channel_buffer.update(&mut cx, |buffer, cx| { -// buffer.buffer().update(cx, |buffer, cx| { -// buffer.set_language_registry(language_registry); -// if let Some(markdown) = markdown { -// buffer.set_language(Some(markdown), cx); -// } -// }) -// }); - -// pane.update(&mut cx, |pane, cx| { -// let buffer_id = channel_buffer.read(cx).remote_id(cx); - -// let existing_view = pane -// .items_of_type::() -// .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id); - -// // If this channel buffer is already open in this pane, just return it. -// if let Some(existing_view) = existing_view.clone() { -// if existing_view.read(cx).channel_buffer == channel_buffer { -// return existing_view; -// } -// } - -// let view = cx.add_view(|cx| { -// let mut this = Self::new(project, channel_store, channel_buffer, cx); -// this.acknowledge_buffer_version(cx); -// this -// }); - -// // If the pane contained a disconnected view for this channel buffer, -// // replace that. -// if let Some(existing_item) = existing_view { -// if let Some(ix) = pane.index_for_item(&existing_item) { -// pane.close_item_by_id(existing_item.id(), SaveIntent::Skip, cx) -// .detach(); -// pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx); -// } -// } - -// view -// }) -// .ok_or_else(|| anyhow!("pane was dropped")) -// }) -// } - -// pub fn new( -// project: ModelHandle, -// channel_store: ModelHandle, -// channel_buffer: ModelHandle, -// cx: &mut ViewContext, -// ) -> Self { -// let buffer = channel_buffer.read(cx).buffer(); -// let editor = cx.add_view(|cx| { -// let mut editor = Editor::for_buffer(buffer, None, cx); -// editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub( -// channel_buffer.clone(), -// ))); -// editor.set_read_only( -// !channel_buffer -// .read(cx) -// .channel(cx) -// .is_some_and(|c| c.can_edit_notes()), -// ); -// editor -// }); -// let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone())); - -// cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event) -// .detach(); - -// Self { -// editor, -// project, -// channel_store, -// channel_buffer, -// remote_id: None, -// _editor_event_subscription, -// } -// } - -// pub fn channel(&self, cx: &AppContext) -> Option> { -// self.channel_buffer.read(cx).channel(cx) -// } - -// fn handle_channel_buffer_event( -// &mut self, -// _: ModelHandle, -// event: &ChannelBufferEvent, -// cx: &mut ViewContext, -// ) { -// match event { -// ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| { -// editor.set_read_only(true); -// cx.notify(); -// }), -// ChannelBufferEvent::ChannelChanged => { -// self.editor.update(cx, |editor, cx| { -// editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes())); -// cx.emit(editor::Event::TitleChanged); -// cx.notify() -// }); -// } -// ChannelBufferEvent::BufferEdited => { -// if cx.is_self_focused() || self.editor.is_focused(cx) { -// self.acknowledge_buffer_version(cx); -// } else { -// self.channel_store.update(cx, |store, cx| { -// let channel_buffer = self.channel_buffer.read(cx); -// store.notes_changed( -// channel_buffer.channel_id, -// channel_buffer.epoch(), -// &channel_buffer.buffer().read(cx).version(), -// cx, -// ) -// }); -// } -// } -// ChannelBufferEvent::CollaboratorsChanged => {} -// } -// } - -// fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext<'_, '_, ChannelView>) { -// self.channel_store.update(cx, |store, cx| { -// let channel_buffer = self.channel_buffer.read(cx); -// store.acknowledge_notes_version( -// channel_buffer.channel_id, -// channel_buffer.epoch(), -// &channel_buffer.buffer().read(cx).version(), -// cx, -// ) -// }); -// self.channel_buffer.update(cx, |buffer, cx| { -// buffer.acknowledge_buffer_version(cx); -// }); -// } -// } - -// impl Entity for ChannelView { -// type Event = editor::Event; -// } - -// impl View for ChannelView { -// fn ui_name() -> &'static str { -// "ChannelView" -// } - -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// ChildView::new(self.editor.as_any(), cx).into_any() -// } - -// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { -// if cx.is_self_focused() { -// self.acknowledge_buffer_version(cx); -// cx.focus(self.editor.as_any()) -// } -// } -// } - -// impl Item for ChannelView { -// fn act_as_type<'a>( -// &'a self, -// type_id: TypeId, -// self_handle: &'a ViewHandle, -// _: &'a AppContext, -// ) -> Option<&'a AnyViewHandle> { -// if type_id == TypeId::of::() { -// Some(self_handle) -// } else if type_id == TypeId::of::() { -// Some(&self.editor) -// } else { -// None -// } -// } - -// fn tab_content( -// &self, -// _: Option, -// style: &theme::Tab, -// cx: &gpui::AppContext, -// ) -> AnyElement { -// let label = if let Some(channel) = self.channel(cx) { -// match ( -// channel.can_edit_notes(), -// self.channel_buffer.read(cx).is_connected(), -// ) { -// (true, true) => format!("#{}", channel.name), -// (false, true) => format!("#{} (read-only)", channel.name), -// (_, false) => format!("#{} (disconnected)", channel.name), -// } -// } else { -// format!("channel notes (disconnected)") -// }; -// Label::new(label, style.label.to_owned()).into_any() -// } - -// fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext) -> Option { -// Some(Self::new( -// self.project.clone(), -// self.channel_store.clone(), -// self.channel_buffer.clone(), -// cx, -// )) -// } - -// fn is_singleton(&self, _cx: &AppContext) -> bool { -// false -// } - -// fn navigate(&mut self, data: Box, cx: &mut ViewContext) -> bool { -// self.editor -// .update(cx, |editor, cx| editor.navigate(data, cx)) -// } - -// fn deactivated(&mut self, cx: &mut ViewContext) { -// self.editor -// .update(cx, |editor, cx| Item::deactivated(editor, cx)) -// } - -// fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext) { -// self.editor -// .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx)) -// } - -// fn as_searchable(&self, _: &ViewHandle) -> Option> { -// Some(Box::new(self.editor.clone())) -// } - -// fn show_toolbar(&self) -> bool { -// true -// } - -// fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option { -// self.editor.read(cx).pixel_position_of_cursor(cx) -// } - -// fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> { -// editor::Editor::to_item_events(event) -// } -// } - -// impl FollowableItem for ChannelView { -// fn remote_id(&self) -> Option { -// self.remote_id -// } - -// fn to_state_proto(&self, cx: &AppContext) -> Option { -// let channel_buffer = self.channel_buffer.read(cx); -// if !channel_buffer.is_connected() { -// return None; -// } - -// Some(proto::view::Variant::ChannelView( -// proto::view::ChannelView { -// channel_id: channel_buffer.channel_id, -// editor: if let Some(proto::view::Variant::Editor(proto)) = -// self.editor.read(cx).to_state_proto(cx) -// { -// Some(proto) -// } else { -// None -// }, -// }, -// )) -// } - -// fn from_state_proto( -// pane: ViewHandle, -// workspace: ViewHandle, -// remote_id: workspace::ViewId, -// state: &mut Option, -// cx: &mut AppContext, -// ) -> Option>>> { -// let Some(proto::view::Variant::ChannelView(_)) = state else { -// return None; -// }; -// let Some(proto::view::Variant::ChannelView(state)) = state.take() else { -// unreachable!() -// }; - -// let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx); - -// Some(cx.spawn(|mut cx| async move { -// let this = open.await?; - -// let task = this -// .update(&mut cx, |this, cx| { -// this.remote_id = Some(remote_id); - -// if let Some(state) = state.editor { -// Some(this.editor.update(cx, |editor, cx| { -// editor.apply_update_proto( -// &this.project, -// proto::update_view::Variant::Editor(proto::update_view::Editor { -// selections: state.selections, -// pending_selection: state.pending_selection, -// scroll_top_anchor: state.scroll_top_anchor, -// scroll_x: state.scroll_x, -// scroll_y: state.scroll_y, -// ..Default::default() -// }), -// cx, -// ) -// })) -// } else { -// None -// } -// }) -// .ok_or_else(|| anyhow!("window was closed"))?; - -// if let Some(task) = task { -// task.await?; -// } - -// Ok(this) -// })) -// } - -// fn add_event_to_update_proto( -// &self, -// event: &Self::Event, -// update: &mut Option, -// cx: &AppContext, -// ) -> bool { -// self.editor -// .read(cx) -// .add_event_to_update_proto(event, update, cx) -// } - -// fn apply_update_proto( -// &mut self, -// project: &ModelHandle, -// message: proto::update_view::Variant, -// cx: &mut ViewContext, -// ) -> gpui::Task> { -// self.editor.update(cx, |editor, cx| { -// editor.apply_update_proto(project, message, cx) -// }) -// } - -// fn set_leader_peer_id(&mut self, leader_peer_id: Option, cx: &mut ViewContext) { -// self.editor.update(cx, |editor, cx| { -// editor.set_leader_peer_id(leader_peer_id, cx) -// }) -// } - -// fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool { -// Editor::should_unfollow_on_event(event, cx) -// } - -// fn is_project_item(&self, _cx: &AppContext) -> bool { -// false -// } -// } - -// struct ChannelBufferCollaborationHub(ModelHandle); - -// impl CollaborationHub for ChannelBufferCollaborationHub { -// fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap { -// self.0.read(cx).collaborators() -// } - -// fn user_participant_indices<'a>( -// &self, -// cx: &'a AppContext, -// ) -> &'a HashMap { -// self.0.read(cx).user_store().read(cx).participant_indices() -// } -// } +use anyhow::Result; +use call::report_call_event_for_channel; +use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore}; +use client::{ + proto::{self, PeerId}, + Collaborator, ParticipantIndex, +}; +use collections::HashMap; +use editor::{CollaborationHub, Editor, EditorEvent}; +use gpui::{ + actions, AnyElement, AnyView, AppContext, Entity as _, EventEmitter, FocusableView, + IntoElement as _, Model, Pixels, Point, Render, Subscription, Task, View, ViewContext, + VisualContext as _, WindowContext, +}; +use project::Project; +use std::{ + any::{Any, TypeId}, + sync::Arc, +}; +use ui::Label; +use util::ResultExt; +use workspace::{ + item::{FollowableItem, Item, ItemEvent, ItemHandle}, + register_followable_item, + searchable::SearchableItemHandle, + ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId, +}; + +actions!(Deploy); + +pub fn init(cx: &mut AppContext) { + register_followable_item::(cx) +} + +pub struct ChannelView { + pub editor: View, + project: Model, + channel_store: Model, + channel_buffer: Model, + remote_id: Option, + _editor_event_subscription: Subscription, +} + +impl ChannelView { + pub fn open( + channel_id: ChannelId, + workspace: View, + cx: &mut WindowContext, + ) -> Task>> { + let pane = workspace.read(cx).active_pane().clone(); + let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx); + cx.spawn(|mut cx| async move { + let channel_view = channel_view.await?; + pane.update(&mut cx, |pane, cx| { + report_call_event_for_channel( + "open channel notes", + channel_id, + &workspace.read(cx).app_state().client, + cx, + ); + pane.add_item(Box::new(channel_view.clone()), true, true, None, cx); + })?; + anyhow::Ok(channel_view) + }) + } + + pub fn open_in_pane( + channel_id: ChannelId, + pane: View, + workspace: View, + cx: &mut WindowContext, + ) -> Task>> { + let workspace = workspace.read(cx); + let project = workspace.project().to_owned(); + let channel_store = ChannelStore::global(cx); + let language_registry = workspace.app_state().languages.clone(); + let markdown = language_registry.language_for_name("Markdown"); + let channel_buffer = + channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx)); + + cx.spawn(|mut cx| async move { + let channel_buffer = channel_buffer.await?; + let markdown = markdown.await.log_err(); + + channel_buffer.update(&mut cx, |buffer, cx| { + buffer.buffer().update(cx, |buffer, cx| { + buffer.set_language_registry(language_registry); + if let Some(markdown) = markdown { + buffer.set_language(Some(markdown), cx); + } + }) + })?; + + pane.update(&mut cx, |pane, cx| { + let buffer_id = channel_buffer.read(cx).remote_id(cx); + + let existing_view = pane + .items_of_type::() + .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id); + + // If this channel buffer is already open in this pane, just return it. + if let Some(existing_view) = existing_view.clone() { + if existing_view.read(cx).channel_buffer == channel_buffer { + return existing_view; + } + } + + let view = cx.build_view(|cx| { + let mut this = Self::new(project, channel_store, channel_buffer, cx); + this.acknowledge_buffer_version(cx); + this + }); + + // If the pane contained a disconnected view for this channel buffer, + // replace that. + if let Some(existing_item) = existing_view { + if let Some(ix) = pane.index_for_item(&existing_item) { + pane.close_item_by_id(existing_item.entity_id(), SaveIntent::Skip, cx) + .detach(); + pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx); + } + } + + view + }) + }) + } + + pub fn new( + project: Model, + channel_store: Model, + channel_buffer: Model, + cx: &mut ViewContext, + ) -> Self { + let buffer = channel_buffer.read(cx).buffer(); + let editor = cx.build_view(|cx| { + let mut editor = Editor::for_buffer(buffer, None, cx); + editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub( + channel_buffer.clone(), + ))); + editor.set_read_only( + !channel_buffer + .read(cx) + .channel(cx) + .is_some_and(|c| c.can_edit_notes()), + ); + editor + }); + let _editor_event_subscription = + cx.subscribe(&editor, |_, _, e: &EditorEvent, cx| cx.emit(e.clone())); + + cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event) + .detach(); + + Self { + editor, + project, + channel_store, + channel_buffer, + remote_id: None, + _editor_event_subscription, + } + } + + pub fn channel(&self, cx: &AppContext) -> Option> { + self.channel_buffer.read(cx).channel(cx) + } + + fn handle_channel_buffer_event( + &mut self, + _: Model, + event: &ChannelBufferEvent, + cx: &mut ViewContext, + ) { + match event { + ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| { + editor.set_read_only(true); + cx.notify(); + }), + ChannelBufferEvent::ChannelChanged => { + self.editor.update(cx, |editor, cx| { + editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes())); + cx.emit(editor::EditorEvent::TitleChanged); + cx.notify() + }); + } + ChannelBufferEvent::BufferEdited => { + if self.editor.read(cx).is_focused(cx) { + self.acknowledge_buffer_version(cx); + } else { + self.channel_store.update(cx, |store, cx| { + let channel_buffer = self.channel_buffer.read(cx); + store.notes_changed( + channel_buffer.channel_id, + channel_buffer.epoch(), + &channel_buffer.buffer().read(cx).version(), + cx, + ) + }); + } + } + ChannelBufferEvent::CollaboratorsChanged => {} + } + } + + fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext) { + self.channel_store.update(cx, |store, cx| { + let channel_buffer = self.channel_buffer.read(cx); + store.acknowledge_notes_version( + channel_buffer.channel_id, + channel_buffer.epoch(), + &channel_buffer.buffer().read(cx).version(), + cx, + ) + }); + self.channel_buffer.update(cx, |buffer, cx| { + buffer.acknowledge_buffer_version(cx); + }); + } +} + +impl EventEmitter for ChannelView {} + +impl Render for ChannelView { + type Element = AnyView; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + self.editor.clone().into() + } +} + +impl FocusableView for ChannelView { + fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + self.editor.read(cx).focus_handle(cx) + } +} + +impl Item for ChannelView { + type Event = EditorEvent; + + fn act_as_type<'a>( + &'a self, + type_id: TypeId, + self_handle: &'a View, + _: &'a AppContext, + ) -> Option { + if type_id == TypeId::of::() { + Some(self_handle.to_any()) + } else if type_id == TypeId::of::() { + Some(self.editor.to_any()) + } else { + None + } + } + + fn tab_content(&self, _: Option, cx: &WindowContext) -> AnyElement { + let label = if let Some(channel) = self.channel(cx) { + match ( + channel.can_edit_notes(), + self.channel_buffer.read(cx).is_connected(), + ) { + (true, true) => format!("#{}", channel.name), + (false, true) => format!("#{} (read-only)", channel.name), + (_, false) => format!("#{} (disconnected)", channel.name), + } + } else { + format!("channel notes (disconnected)") + }; + Label::new(label).into_any_element() + } + + fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext) -> Option> { + Some(cx.build_view(|cx| { + Self::new( + self.project.clone(), + self.channel_store.clone(), + self.channel_buffer.clone(), + cx, + ) + })) + } + + fn is_singleton(&self, _cx: &AppContext) -> bool { + false + } + + fn navigate(&mut self, data: Box, cx: &mut ViewContext) -> bool { + self.editor + .update(cx, |editor, cx| editor.navigate(data, cx)) + } + + fn deactivated(&mut self, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| Item::deactivated(editor, cx)) + } + + fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx)) + } + + fn as_searchable(&self, _: &View) -> Option> { + Some(Box::new(self.editor.clone())) + } + + fn show_toolbar(&self) -> bool { + true + } + + fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option> { + self.editor.read(cx).pixel_position_of_cursor(cx) + } + + fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) { + Editor::to_item_events(event, f) + } +} + +impl FollowableItem for ChannelView { + fn remote_id(&self) -> Option { + self.remote_id + } + + fn to_state_proto(&self, cx: &WindowContext) -> Option { + let channel_buffer = self.channel_buffer.read(cx); + if !channel_buffer.is_connected() { + return None; + } + + Some(proto::view::Variant::ChannelView( + proto::view::ChannelView { + channel_id: channel_buffer.channel_id, + editor: if let Some(proto::view::Variant::Editor(proto)) = + self.editor.read(cx).to_state_proto(cx) + { + Some(proto) + } else { + None + }, + }, + )) + } + + fn from_state_proto( + pane: View, + workspace: View, + remote_id: workspace::ViewId, + state: &mut Option, + cx: &mut WindowContext, + ) -> Option>>> { + let Some(proto::view::Variant::ChannelView(_)) = state else { + return None; + }; + let Some(proto::view::Variant::ChannelView(state)) = state.take() else { + unreachable!() + }; + + let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx); + + Some(cx.spawn(|mut cx| async move { + let this = open.await?; + + let task = this.update(&mut cx, |this, cx| { + this.remote_id = Some(remote_id); + + if let Some(state) = state.editor { + Some(this.editor.update(cx, |editor, cx| { + editor.apply_update_proto( + &this.project, + proto::update_view::Variant::Editor(proto::update_view::Editor { + selections: state.selections, + pending_selection: state.pending_selection, + scroll_top_anchor: state.scroll_top_anchor, + scroll_x: state.scroll_x, + scroll_y: state.scroll_y, + ..Default::default() + }), + cx, + ) + })) + } else { + None + } + })?; + + if let Some(task) = task { + task.await?; + } + + Ok(this) + })) + } + + fn add_event_to_update_proto( + &self, + event: &EditorEvent, + update: &mut Option, + cx: &WindowContext, + ) -> bool { + self.editor + .read(cx) + .add_event_to_update_proto(event, update, cx) + } + + fn apply_update_proto( + &mut self, + project: &Model, + message: proto::update_view::Variant, + cx: &mut ViewContext, + ) -> gpui::Task> { + self.editor.update(cx, |editor, cx| { + editor.apply_update_proto(project, message, cx) + }) + } + + fn set_leader_peer_id(&mut self, leader_peer_id: Option, cx: &mut ViewContext) { + self.editor.update(cx, |editor, cx| { + editor.set_leader_peer_id(leader_peer_id, cx) + }) + } + + fn is_project_item(&self, _cx: &WindowContext) -> bool { + false + } + + fn to_follow_event(event: &Self::Event) -> Option { + Editor::to_follow_event(event) + } +} + +struct ChannelBufferCollaborationHub(Model); + +impl CollaborationHub for ChannelBufferCollaborationHub { + fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap { + self.0.read(cx).collaborators() + } + + fn user_participant_indices<'a>( + &self, + cx: &'a AppContext, + ) -> &'a HashMap { + self.0.read(cx).user_store().read(cx).participant_indices() + } +} diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 8c0a92ad5252c5b12bc56ee174329a15a5a384c2..272f02bb49105bc55c2b4ca8a0755a4e045e0342 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -191,6 +191,7 @@ use workspace::{ Workspace, }; +use crate::channel_view::ChannelView; use crate::{face_pile::FacePile, CollaborationPanelSettings}; use self::channel_modal::ChannelModal; @@ -1935,8 +1936,7 @@ impl CollabPanel { fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext) { if let Some(workspace) = self.workspace.upgrade() { - todo!(); - // ChannelView::open(action.channel_id, workspace, cx).detach(); + ChannelView::open(channel_id, workspace, cx).detach(); } }