2023-08-24 21:29:04 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
2023-09-26 18:18:32 +00:00
|
|
|
use call::report_call_event_for_channel;
|
2023-09-21 23:13:12 +00:00
|
|
|
use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId};
|
2023-09-26 21:19:38 +00:00
|
|
|
use client::{
|
|
|
|
proto::{self, PeerId},
|
2023-09-28 18:18:29 +00:00
|
|
|
Collaborator, ParticipantIndex,
|
2023-09-26 21:19:38 +00:00
|
|
|
};
|
2023-08-23 01:08:03 +00:00
|
|
|
use collections::HashMap;
|
2023-09-26 21:19:38 +00:00
|
|
|
use editor::{CollaborationHub, Editor};
|
2023-08-22 21:18:32 +00:00
|
|
|
use gpui::{
|
|
|
|
actions,
|
|
|
|
elements::{ChildView, Label},
|
2023-08-25 00:18:18 +00:00
|
|
|
geometry::vector::Vector2F,
|
2023-08-24 21:29:04 +00:00
|
|
|
AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Subscription, Task, View,
|
2023-08-24 18:25:20 +00:00
|
|
|
ViewContext, ViewHandle,
|
2023-08-22 21:18:32 +00:00
|
|
|
};
|
2023-08-23 01:08:03 +00:00
|
|
|
use project::Project;
|
2023-09-21 23:13:12 +00:00
|
|
|
use std::{
|
|
|
|
any::{Any, TypeId},
|
|
|
|
sync::Arc,
|
|
|
|
};
|
2023-09-20 01:03:38 +00:00
|
|
|
use util::ResultExt;
|
2023-08-24 18:25:20 +00:00
|
|
|
use workspace::{
|
|
|
|
item::{FollowableItem, Item, ItemHandle},
|
2023-08-25 00:18:18 +00:00
|
|
|
register_followable_item,
|
|
|
|
searchable::SearchableItemHandle,
|
|
|
|
ItemNavHistory, Pane, ViewId, Workspace, WorkspaceId,
|
2023-08-24 18:25:20 +00:00
|
|
|
};
|
2023-08-22 21:18:32 +00:00
|
|
|
|
|
|
|
actions!(channel_view, [Deploy]);
|
|
|
|
|
2023-09-21 23:13:12 +00:00
|
|
|
pub fn init(cx: &mut AppContext) {
|
2023-08-24 18:25:20 +00:00
|
|
|
register_followable_item::<ChannelView>(cx)
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ChannelView {
|
2023-08-23 20:32:16 +00:00
|
|
|
pub editor: ViewHandle<Editor>,
|
2023-08-23 01:08:03 +00:00
|
|
|
project: ModelHandle<Project>,
|
2023-08-22 21:18:32 +00:00
|
|
|
channel_buffer: ModelHandle<ChannelBuffer>,
|
2023-08-24 18:25:20 +00:00
|
|
|
remote_id: Option<ViewId>,
|
|
|
|
_editor_event_subscription: Subscription,
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ChannelView {
|
2023-09-21 23:13:12 +00:00
|
|
|
pub fn open(
|
|
|
|
channel_id: ChannelId,
|
|
|
|
workspace: ViewHandle<Workspace>,
|
|
|
|
cx: &mut AppContext,
|
|
|
|
) -> Task<Result<ViewHandle<Self>>> {
|
2023-09-15 01:05:44 +00:00
|
|
|
let pane = workspace.read(cx).active_pane().clone();
|
2023-09-21 23:13:12 +00:00
|
|
|
let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx);
|
2023-09-15 01:05:44 +00:00
|
|
|
cx.spawn(|mut cx| async move {
|
|
|
|
let channel_view = channel_view.await?;
|
|
|
|
pane.update(&mut cx, |pane, cx| {
|
2023-09-26 18:18:32 +00:00
|
|
|
report_call_event_for_channel(
|
2023-09-15 01:05:44 +00:00
|
|
|
"open channel notes",
|
2023-09-26 18:18:32 +00:00
|
|
|
channel_id,
|
2023-09-15 01:05:44 +00:00
|
|
|
&workspace.read(cx).app_state().client,
|
|
|
|
cx,
|
|
|
|
);
|
2023-09-20 01:03:38 +00:00
|
|
|
pane.add_item(Box::new(channel_view.clone()), true, true, None, cx);
|
2023-09-15 01:05:44 +00:00
|
|
|
});
|
2023-09-21 23:13:12 +00:00
|
|
|
anyhow::Ok(channel_view)
|
2023-09-15 01:05:44 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-21 23:13:12 +00:00
|
|
|
pub fn open_in_pane(
|
2023-08-24 21:29:04 +00:00
|
|
|
channel_id: ChannelId,
|
|
|
|
pane: ViewHandle<Pane>,
|
|
|
|
workspace: ViewHandle<Workspace>,
|
|
|
|
cx: &mut AppContext,
|
|
|
|
) -> Task<Result<ViewHandle<Self>>> {
|
|
|
|
let workspace = workspace.read(cx);
|
|
|
|
let project = workspace.project().to_owned();
|
|
|
|
let channel_store = workspace.app_state().channel_store.clone();
|
|
|
|
let markdown = workspace
|
|
|
|
.app_state()
|
|
|
|
.languages
|
|
|
|
.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?;
|
2023-09-11 20:44:41 +00:00
|
|
|
|
2023-09-20 01:03:38 +00:00
|
|
|
if let Some(markdown) = markdown.await.log_err() {
|
|
|
|
channel_buffer.update(&mut cx, |buffer, cx| {
|
|
|
|
buffer.buffer().update(cx, |buffer, cx| {
|
|
|
|
buffer.set_language(Some(markdown), cx);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
2023-08-24 21:29:04 +00:00
|
|
|
|
|
|
|
pane.update(&mut cx, |pane, cx| {
|
|
|
|
pane.items_of_type::<Self>()
|
|
|
|
.find(|channel_view| channel_view.read(cx).channel_buffer == channel_buffer)
|
|
|
|
.unwrap_or_else(|| cx.add_view(|cx| Self::new(project, channel_buffer, cx)))
|
|
|
|
})
|
|
|
|
.ok_or_else(|| anyhow!("pane was dropped"))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-22 21:18:32 +00:00
|
|
|
pub fn new(
|
2023-08-23 01:08:03 +00:00
|
|
|
project: ModelHandle<Project>,
|
2023-08-22 21:18:32 +00:00
|
|
|
channel_buffer: ModelHandle<ChannelBuffer>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Self {
|
|
|
|
let buffer = channel_buffer.read(cx).buffer();
|
2023-09-26 21:19:38 +00:00
|
|
|
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
|
|
|
|
});
|
2023-08-24 18:25:20 +00:00
|
|
|
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
|
|
|
|
|
2023-08-23 20:32:16 +00:00
|
|
|
cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event)
|
|
|
|
.detach();
|
|
|
|
|
2023-09-26 21:19:38 +00:00
|
|
|
Self {
|
2023-08-22 21:18:32 +00:00
|
|
|
editor,
|
2023-08-23 01:08:03 +00:00
|
|
|
project,
|
2023-08-22 21:18:32 +00:00
|
|
|
channel_buffer,
|
2023-08-24 18:25:20 +00:00
|
|
|
remote_id: None,
|
|
|
|
_editor_event_subscription,
|
2023-09-26 21:19:38 +00:00
|
|
|
}
|
2023-08-23 01:08:03 +00:00
|
|
|
}
|
|
|
|
|
2023-09-21 23:13:12 +00:00
|
|
|
pub fn channel(&self, cx: &AppContext) -> Arc<Channel> {
|
|
|
|
self.channel_buffer.read(cx).channel()
|
|
|
|
}
|
|
|
|
|
2023-08-23 20:32:16 +00:00
|
|
|
fn handle_channel_buffer_event(
|
|
|
|
&mut self,
|
|
|
|
_: ModelHandle<ChannelBuffer>,
|
2023-09-07 18:16:51 +00:00
|
|
|
event: &ChannelBufferEvent,
|
2023-08-23 20:32:16 +00:00
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) {
|
2023-09-26 21:19:38 +00:00
|
|
|
if let ChannelBufferEvent::Disconnected = event {
|
|
|
|
self.editor.update(cx, |editor, cx| {
|
2023-08-24 23:50:13 +00:00
|
|
|
editor.set_read_only(true);
|
|
|
|
cx.notify();
|
2023-09-26 21:19:38 +00:00
|
|
|
})
|
2023-08-24 23:50:13 +00:00
|
|
|
}
|
2023-08-23 20:32:16 +00:00
|
|
|
}
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Entity for ChannelView {
|
|
|
|
type Event = editor::Event;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl View for ChannelView {
|
|
|
|
fn ui_name() -> &'static str {
|
|
|
|
"ChannelView"
|
|
|
|
}
|
|
|
|
|
2023-08-24 18:25:20 +00:00
|
|
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
2023-08-22 21:18:32 +00:00
|
|
|
ChildView::new(self.editor.as_any(), cx).into_any()
|
|
|
|
}
|
2023-08-24 18:25:20 +00:00
|
|
|
|
|
|
|
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
|
|
if cx.is_self_focused() {
|
|
|
|
cx.focus(self.editor.as_any())
|
|
|
|
}
|
|
|
|
}
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Item for ChannelView {
|
2023-09-08 09:20:49 +00:00
|
|
|
fn act_as_type<'a>(
|
|
|
|
&'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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-22 21:18:32 +00:00
|
|
|
fn tab_content<V: 'static>(
|
|
|
|
&self,
|
|
|
|
_: Option<usize>,
|
|
|
|
style: &theme::Tab,
|
|
|
|
cx: &gpui::AppContext,
|
|
|
|
) -> AnyElement<V> {
|
2023-08-24 23:50:13 +00:00
|
|
|
let channel_name = &self.channel_buffer.read(cx).channel().name;
|
|
|
|
let label = if self.channel_buffer.read(cx).is_connected() {
|
|
|
|
format!("#{}", channel_name)
|
|
|
|
} else {
|
|
|
|
format!("#{} (disconnected)", channel_name)
|
|
|
|
};
|
|
|
|
Label::new(label, style.label.to_owned()).into_any()
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
2023-08-24 21:29:04 +00:00
|
|
|
|
|
|
|
fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self> {
|
|
|
|
Some(Self::new(
|
|
|
|
self.project.clone(),
|
|
|
|
self.channel_buffer.clone(),
|
|
|
|
cx,
|
|
|
|
))
|
|
|
|
}
|
2023-08-25 00:18:18 +00:00
|
|
|
|
|
|
|
fn is_singleton(&self, _cx: &AppContext) -> bool {
|
2023-08-30 22:05:47 +00:00
|
|
|
false
|
2023-08-25 00:18:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
|
|
|
|
self.editor
|
|
|
|
.update(cx, |editor, cx| editor.navigate(data, cx))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
|
|
|
self.editor
|
|
|
|
.update(cx, |editor, cx| Item::deactivated(editor, cx))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
|
|
|
self.editor
|
|
|
|
.update(cx, |editor, cx| Item::set_nav_history(editor, history, cx))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
|
|
|
Some(Box::new(self.editor.clone()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn show_toolbar(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
|
|
|
|
self.editor.read(cx).pixel_position_of_cursor(cx)
|
|
|
|
}
|
2023-08-22 21:18:32 +00:00
|
|
|
}
|
2023-08-24 18:25:20 +00:00
|
|
|
|
|
|
|
impl FollowableItem for ChannelView {
|
|
|
|
fn remote_id(&self) -> Option<workspace::ViewId> {
|
|
|
|
self.remote_id
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
|
2023-08-24 23:50:13 +00:00
|
|
|
let channel = self.channel_buffer.read(cx).channel();
|
|
|
|
Some(proto::view::Variant::ChannelView(
|
|
|
|
proto::view::ChannelView {
|
2023-08-24 18:25:20 +00:00
|
|
|
channel_id: channel.id,
|
2023-08-24 19:36:01 +00:00
|
|
|
editor: if let Some(proto::view::Variant::Editor(proto)) =
|
|
|
|
self.editor.read(cx).to_state_proto(cx)
|
|
|
|
{
|
|
|
|
Some(proto)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2023-08-24 23:50:13 +00:00
|
|
|
},
|
|
|
|
))
|
2023-08-24 18:25:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn from_state_proto(
|
2023-08-24 21:29:04 +00:00
|
|
|
pane: ViewHandle<workspace::Pane>,
|
2023-08-24 18:25:20 +00:00
|
|
|
workspace: ViewHandle<workspace::Workspace>,
|
|
|
|
remote_id: workspace::ViewId,
|
2023-08-24 19:36:01 +00:00
|
|
|
state: &mut Option<proto::view::Variant>,
|
2023-08-24 18:25:20 +00:00
|
|
|
cx: &mut AppContext,
|
|
|
|
) -> Option<gpui::Task<anyhow::Result<ViewHandle<Self>>>> {
|
2023-08-25 17:11:32 +00:00
|
|
|
let Some(proto::view::Variant::ChannelView(_)) = state else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
let Some(proto::view::Variant::ChannelView(state)) = state.take() else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2023-08-24 18:25:20 +00:00
|
|
|
|
2023-09-21 23:13:12 +00:00
|
|
|
let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx);
|
2023-08-24 18:25:20 +00:00
|
|
|
|
|
|
|
Some(cx.spawn(|mut cx| async move {
|
2023-08-24 21:29:04 +00:00
|
|
|
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
|
|
|
|
}
|
2023-08-24 18:25:20 +00:00
|
|
|
})
|
2023-08-24 21:29:04 +00:00
|
|
|
.ok_or_else(|| anyhow!("window was closed"))?;
|
|
|
|
|
|
|
|
if let Some(task) = task {
|
|
|
|
task.await?;
|
2023-08-24 19:36:01 +00:00
|
|
|
}
|
2023-08-24 18:25:20 +00:00
|
|
|
|
|
|
|
Ok(this)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_event_to_update_proto(
|
|
|
|
&self,
|
2023-08-24 19:36:01 +00:00
|
|
|
event: &Self::Event,
|
|
|
|
update: &mut Option<proto::update_view::Variant>,
|
|
|
|
cx: &AppContext,
|
2023-08-24 18:25:20 +00:00
|
|
|
) -> bool {
|
2023-08-24 19:36:01 +00:00
|
|
|
self.editor
|
|
|
|
.read(cx)
|
|
|
|
.add_event_to_update_proto(event, update, cx)
|
2023-08-24 18:25:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn apply_update_proto(
|
|
|
|
&mut self,
|
2023-08-24 19:36:01 +00:00
|
|
|
project: &ModelHandle<Project>,
|
|
|
|
message: proto::update_view::Variant,
|
|
|
|
cx: &mut ViewContext<Self>,
|
2023-08-24 18:25:20 +00:00
|
|
|
) -> gpui::Task<anyhow::Result<()>> {
|
2023-08-24 19:36:01 +00:00
|
|
|
self.editor.update(cx, |editor, cx| {
|
|
|
|
editor.apply_update_proto(project, message, cx)
|
|
|
|
})
|
2023-08-24 18:25:20 +00:00
|
|
|
}
|
|
|
|
|
2023-09-26 21:19:38 +00:00
|
|
|
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
|
2023-08-24 18:25:20 +00:00
|
|
|
self.editor.update(cx, |editor, cx| {
|
2023-09-26 21:19:38 +00:00
|
|
|
editor.set_leader_peer_id(leader_peer_id, cx)
|
2023-08-24 18:25:20 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool {
|
|
|
|
Editor::should_unfollow_on_event(event, cx)
|
|
|
|
}
|
2023-09-21 23:13:12 +00:00
|
|
|
|
|
|
|
fn is_project_item(&self, _cx: &AppContext) -> bool {
|
|
|
|
false
|
|
|
|
}
|
2023-08-24 18:25:20 +00:00
|
|
|
}
|
2023-09-26 21:19:38 +00:00
|
|
|
|
|
|
|
struct ChannelBufferCollaborationHub(ModelHandle<ChannelBuffer>);
|
|
|
|
|
|
|
|
impl CollaborationHub for ChannelBufferCollaborationHub {
|
|
|
|
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
|
|
|
|
self.0.read(cx).collaborators()
|
|
|
|
}
|
|
|
|
|
2023-09-28 18:18:29 +00:00
|
|
|
fn user_participant_indices<'a>(
|
|
|
|
&self,
|
|
|
|
cx: &'a AppContext,
|
|
|
|
) -> &'a HashMap<u64, ParticipantIndex> {
|
|
|
|
self.0.read(cx).user_store().read(cx).participant_indices()
|
2023-09-26 21:19:38 +00:00
|
|
|
}
|
|
|
|
}
|