Bring back channel notes (#3506)

This commit is contained in:
Max Brunsfeld 2023-12-06 12:45:46 -08:00 committed by GitHub
commit dba94c5122
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 1304 additions and 1301 deletions

2
Cargo.lock generated
View file

@ -1830,7 +1830,7 @@ dependencies = [
"clap 3.2.25", "clap 3.2.25",
"client2", "client2",
"clock", "clock",
"collab_ui", "collab_ui2",
"collections", "collections",
"ctor", "ctor",
"dashmap", "dashmap",

View file

@ -81,7 +81,7 @@ settings = { package = "settings2", path = "../settings2", features = ["test-sup
theme = { package = "theme2", path = "../theme2" } theme = { package = "theme2", path = "../theme2" }
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] } workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
collab_ui = { path = "../collab_ui", features = ["test-support"] } collab_ui = { path = "../collab_ui2", package = "collab_ui2", features = ["test-support"] }
async-trait.workspace = true async-trait.workspace = true
pretty_assertions.workspace = true pretty_assertions.workspace = true

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ use client::{
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use fs::FakeFs; use fs::FakeFs;
use futures::{channel::oneshot, StreamExt as _}; use futures::{channel::oneshot, StreamExt as _};
use gpui::{BackgroundExecutor, Context, Model, TestAppContext, WindowHandle}; use gpui::{BackgroundExecutor, Context, Model, TestAppContext, View, VisualTestContext};
use language::LanguageRegistry; use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime; use node_runtime::FakeNodeRuntime;
@ -602,14 +602,12 @@ impl TestClient {
.unwrap() .unwrap()
} }
//todo(workspace) pub fn build_workspace<'a>(
#[allow(dead_code)] &'a self,
pub fn build_workspace(
&self,
project: &Model<Project>, project: &Model<Project>,
cx: &mut TestAppContext, cx: &'a mut TestAppContext,
) -> WindowHandle<Workspace> { ) -> (View<Workspace>, &'a mut VisualTestContext) {
cx.add_window(|cx| Workspace::new(0, project.clone(), self.app_state.clone(), cx)) cx.add_window_view(|cx| Workspace::new(0, project.clone(), self.app_state.clone(), cx))
} }
} }

View file

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

View file

@ -191,6 +191,7 @@ use workspace::{
Workspace, Workspace,
}; };
use crate::channel_view::ChannelView;
use crate::{face_pile::FacePile, CollaborationPanelSettings}; use crate::{face_pile::FacePile, CollaborationPanelSettings};
use self::channel_modal::ChannelModal; use self::channel_modal::ChannelModal;
@ -1935,8 +1936,7 @@ impl CollabPanel {
fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) { fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
if let Some(workspace) = self.workspace.upgrade() { if let Some(workspace) = self.workspace.upgrade() {
todo!(); ChannelView::open(channel_id, workspace, cx).detach();
// ChannelView::open(action.channel_id, workspace, cx).detach();
} }
} }
@ -2619,6 +2619,9 @@ impl CollabPanel {
} else { } else {
Color::Muted Color::Muted
}) })
.on_click(cx.listener(move |this, _, cx| {
this.open_channel_notes(channel_id, cx)
}))
.tooltip(|cx| { .tooltip(|cx| {
Tooltip::text("Open channel notes", cx) Tooltip::text("Open channel notes", cx)
}), }),

View file

@ -33,6 +33,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
// vcs_menu::init(cx); // vcs_menu::init(cx);
collab_titlebar_item::init(cx); collab_titlebar_item::init(cx);
collab_panel::init(cx); collab_panel::init(cx);
channel_view::init(cx);
// chat_panel::init(cx); // chat_panel::init(cx);
notifications::init(&app_state, cx); notifications::init(&app_state, cx);

View file

@ -545,6 +545,10 @@ pub struct VisualTestContext<'a> {
} }
impl<'a> VisualTestContext<'a> { impl<'a> VisualTestContext<'a> {
pub fn update<R>(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R {
self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
}
pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self { pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self {
Self { cx, window } Self { cx, window }
} }

View file

@ -2077,6 +2077,7 @@ impl Workspace {
} }
if &pane == self.active_pane() { if &pane == self.active_pane() {
self.active_item_path_changed(cx); self.active_item_path_changed(cx);
self.update_active_view_for_followers(cx);
} }
} }
pane::Event::ChangeItemTitle => { pane::Event::ChangeItemTitle => {
@ -2756,11 +2757,10 @@ impl Workspace {
fn update_active_view_for_followers(&mut self, cx: &mut ViewContext<Self>) { fn update_active_view_for_followers(&mut self, cx: &mut ViewContext<Self>) {
let mut is_project_item = true; let mut is_project_item = true;
let mut update = proto::UpdateActiveView::default(); let mut update = proto::UpdateActiveView::default();
if self.active_pane.read(cx).has_focus(cx) {
let item = self if let Some(item) = self.active_item(cx) {
.active_item(cx) if item.focus_handle(cx).contains_focused(cx) {
.and_then(|item| item.to_followable_item_handle(cx)); if let Some(item) = item.to_followable_item_handle(cx) {
if let Some(item) = item {
is_project_item = item.is_project_item(cx); is_project_item = item.is_project_item(cx);
update = proto::UpdateActiveView { update = proto::UpdateActiveView {
id: item id: item
@ -2770,6 +2770,7 @@ impl Workspace {
}; };
} }
} }
}
if update.id != self.last_active_view_id { if update.id != self.last_active_view_id {
self.last_active_view_id = update.id.clone(); self.last_active_view_id = update.id.clone();