1// use anyhow::{anyhow, Result};
2// use call::report_call_event_for_channel;
3// use channel::{Channel, ChannelBuffer, ChannelBufferEvent, ChannelId, ChannelStore};
4// use client::{
5// proto::{self, PeerId},
6// Collaborator, ParticipantIndex,
7// };
8// use collections::HashMap;
9// use editor::{CollaborationHub, Editor};
10// use gpui::{
11// actions,
12// elements::{ChildView, Label},
13// geometry::vector::Vector2F,
14// AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Subscription, Task, View,
15// ViewContext, ViewHandle,
16// };
17// use project::Project;
18// use smallvec::SmallVec;
19// use std::{
20// any::{Any, TypeId},
21// sync::Arc,
22// };
23// use util::ResultExt;
24// use workspace::{
25// item::{FollowableItem, Item, ItemEvent, ItemHandle},
26// register_followable_item,
27// searchable::SearchableItemHandle,
28// ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
29// };
30
31// actions!(channel_view, [Deploy]);
32
33// pub fn init(cx: &mut AppContext) {
34// register_followable_item::<ChannelView>(cx)
35// }
36
37// pub struct ChannelView {
38// pub editor: ViewHandle<Editor>,
39// project: ModelHandle<Project>,
40// channel_store: ModelHandle<ChannelStore>,
41// channel_buffer: ModelHandle<ChannelBuffer>,
42// remote_id: Option<ViewId>,
43// _editor_event_subscription: Subscription,
44// }
45
46// impl ChannelView {
47// pub fn open(
48// channel_id: ChannelId,
49// workspace: ViewHandle<Workspace>,
50// cx: &mut AppContext,
51// ) -> Task<Result<ViewHandle<Self>>> {
52// let pane = workspace.read(cx).active_pane().clone();
53// let channel_view = Self::open_in_pane(channel_id, pane.clone(), workspace.clone(), cx);
54// cx.spawn(|mut cx| async move {
55// let channel_view = channel_view.await?;
56// pane.update(&mut cx, |pane, cx| {
57// report_call_event_for_channel(
58// "open channel notes",
59// channel_id,
60// &workspace.read(cx).app_state().client,
61// cx,
62// );
63// pane.add_item(Box::new(channel_view.clone()), true, true, None, cx);
64// });
65// anyhow::Ok(channel_view)
66// })
67// }
68
69// pub fn open_in_pane(
70// channel_id: ChannelId,
71// pane: ViewHandle<Pane>,
72// workspace: ViewHandle<Workspace>,
73// cx: &mut AppContext,
74// ) -> Task<Result<ViewHandle<Self>>> {
75// let workspace = workspace.read(cx);
76// let project = workspace.project().to_owned();
77// let channel_store = ChannelStore::global(cx);
78// let language_registry = workspace.app_state().languages.clone();
79// let markdown = language_registry.language_for_name("Markdown");
80// let channel_buffer =
81// channel_store.update(cx, |store, cx| store.open_channel_buffer(channel_id, cx));
82
83// cx.spawn(|mut cx| async move {
84// let channel_buffer = channel_buffer.await?;
85// let markdown = markdown.await.log_err();
86
87// channel_buffer.update(&mut cx, |buffer, cx| {
88// buffer.buffer().update(cx, |buffer, cx| {
89// buffer.set_language_registry(language_registry);
90// if let Some(markdown) = markdown {
91// buffer.set_language(Some(markdown), cx);
92// }
93// })
94// });
95
96// pane.update(&mut cx, |pane, cx| {
97// let buffer_id = channel_buffer.read(cx).remote_id(cx);
98
99// let existing_view = pane
100// .items_of_type::<Self>()
101// .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id);
102
103// // If this channel buffer is already open in this pane, just return it.
104// if let Some(existing_view) = existing_view.clone() {
105// if existing_view.read(cx).channel_buffer == channel_buffer {
106// return existing_view;
107// }
108// }
109
110// let view = cx.add_view(|cx| {
111// let mut this = Self::new(project, channel_store, channel_buffer, cx);
112// this.acknowledge_buffer_version(cx);
113// this
114// });
115
116// // If the pane contained a disconnected view for this channel buffer,
117// // replace that.
118// if let Some(existing_item) = existing_view {
119// if let Some(ix) = pane.index_for_item(&existing_item) {
120// pane.close_item_by_id(existing_item.id(), SaveIntent::Skip, cx)
121// .detach();
122// pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx);
123// }
124// }
125
126// view
127// })
128// .ok_or_else(|| anyhow!("pane was dropped"))
129// })
130// }
131
132// pub fn new(
133// project: ModelHandle<Project>,
134// channel_store: ModelHandle<ChannelStore>,
135// channel_buffer: ModelHandle<ChannelBuffer>,
136// cx: &mut ViewContext<Self>,
137// ) -> Self {
138// let buffer = channel_buffer.read(cx).buffer();
139// let editor = cx.add_view(|cx| {
140// let mut editor = Editor::for_buffer(buffer, None, cx);
141// editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
142// channel_buffer.clone(),
143// )));
144// editor.set_read_only(
145// !channel_buffer
146// .read(cx)
147// .channel(cx)
148// .is_some_and(|c| c.can_edit_notes()),
149// );
150// editor
151// });
152// let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
153
154// cx.subscribe(&channel_buffer, Self::handle_channel_buffer_event)
155// .detach();
156
157// Self {
158// editor,
159// project,
160// channel_store,
161// channel_buffer,
162// remote_id: None,
163// _editor_event_subscription,
164// }
165// }
166
167// pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
168// self.channel_buffer.read(cx).channel(cx)
169// }
170
171// fn handle_channel_buffer_event(
172// &mut self,
173// _: ModelHandle<ChannelBuffer>,
174// event: &ChannelBufferEvent,
175// cx: &mut ViewContext<Self>,
176// ) {
177// match event {
178// ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
179// editor.set_read_only(true);
180// cx.notify();
181// }),
182// ChannelBufferEvent::ChannelChanged => {
183// self.editor.update(cx, |editor, cx| {
184// editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
185// cx.emit(editor::Event::TitleChanged);
186// cx.notify()
187// });
188// }
189// ChannelBufferEvent::BufferEdited => {
190// if cx.is_self_focused() || self.editor.is_focused(cx) {
191// self.acknowledge_buffer_version(cx);
192// } else {
193// self.channel_store.update(cx, |store, cx| {
194// let channel_buffer = self.channel_buffer.read(cx);
195// store.notes_changed(
196// channel_buffer.channel_id,
197// channel_buffer.epoch(),
198// &channel_buffer.buffer().read(cx).version(),
199// cx,
200// )
201// });
202// }
203// }
204// ChannelBufferEvent::CollaboratorsChanged => {}
205// }
206// }
207
208// fn acknowledge_buffer_version(&mut self, cx: &mut ViewContext<'_, '_, ChannelView>) {
209// self.channel_store.update(cx, |store, cx| {
210// let channel_buffer = self.channel_buffer.read(cx);
211// store.acknowledge_notes_version(
212// channel_buffer.channel_id,
213// channel_buffer.epoch(),
214// &channel_buffer.buffer().read(cx).version(),
215// cx,
216// )
217// });
218// self.channel_buffer.update(cx, |buffer, cx| {
219// buffer.acknowledge_buffer_version(cx);
220// });
221// }
222// }
223
224// impl Entity for ChannelView {
225// type Event = editor::Event;
226// }
227
228// impl View for ChannelView {
229// fn ui_name() -> &'static str {
230// "ChannelView"
231// }
232
233// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
234// ChildView::new(self.editor.as_any(), cx).into_any()
235// }
236
237// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
238// if cx.is_self_focused() {
239// self.acknowledge_buffer_version(cx);
240// cx.focus(self.editor.as_any())
241// }
242// }
243// }
244
245// impl Item for ChannelView {
246// fn act_as_type<'a>(
247// &'a self,
248// type_id: TypeId,
249// self_handle: &'a ViewHandle<Self>,
250// _: &'a AppContext,
251// ) -> Option<&'a AnyViewHandle> {
252// if type_id == TypeId::of::<Self>() {
253// Some(self_handle)
254// } else if type_id == TypeId::of::<Editor>() {
255// Some(&self.editor)
256// } else {
257// None
258// }
259// }
260
261// fn tab_content<V: 'static>(
262// &self,
263// _: Option<usize>,
264// style: &theme::Tab,
265// cx: &gpui::AppContext,
266// ) -> AnyElement<V> {
267// let label = if let Some(channel) = self.channel(cx) {
268// match (
269// channel.can_edit_notes(),
270// self.channel_buffer.read(cx).is_connected(),
271// ) {
272// (true, true) => format!("#{}", channel.name),
273// (false, true) => format!("#{} (read-only)", channel.name),
274// (_, false) => format!("#{} (disconnected)", channel.name),
275// }
276// } else {
277// format!("channel notes (disconnected)")
278// };
279// Label::new(label, style.label.to_owned()).into_any()
280// }
281
282// fn clone_on_split(&self, _: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self> {
283// Some(Self::new(
284// self.project.clone(),
285// self.channel_store.clone(),
286// self.channel_buffer.clone(),
287// cx,
288// ))
289// }
290
291// fn is_singleton(&self, _cx: &AppContext) -> bool {
292// false
293// }
294
295// fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
296// self.editor
297// .update(cx, |editor, cx| editor.navigate(data, cx))
298// }
299
300// fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
301// self.editor
302// .update(cx, |editor, cx| Item::deactivated(editor, cx))
303// }
304
305// fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
306// self.editor
307// .update(cx, |editor, cx| Item::set_nav_history(editor, history, cx))
308// }
309
310// fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
311// Some(Box::new(self.editor.clone()))
312// }
313
314// fn show_toolbar(&self) -> bool {
315// true
316// }
317
318// fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
319// self.editor.read(cx).pixel_position_of_cursor(cx)
320// }
321
322// fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
323// editor::Editor::to_item_events(event)
324// }
325// }
326
327// impl FollowableItem for ChannelView {
328// fn remote_id(&self) -> Option<workspace::ViewId> {
329// self.remote_id
330// }
331
332// fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
333// let channel_buffer = self.channel_buffer.read(cx);
334// if !channel_buffer.is_connected() {
335// return None;
336// }
337
338// Some(proto::view::Variant::ChannelView(
339// proto::view::ChannelView {
340// channel_id: channel_buffer.channel_id,
341// editor: if let Some(proto::view::Variant::Editor(proto)) =
342// self.editor.read(cx).to_state_proto(cx)
343// {
344// Some(proto)
345// } else {
346// None
347// },
348// },
349// ))
350// }
351
352// fn from_state_proto(
353// pane: ViewHandle<workspace::Pane>,
354// workspace: ViewHandle<workspace::Workspace>,
355// remote_id: workspace::ViewId,
356// state: &mut Option<proto::view::Variant>,
357// cx: &mut AppContext,
358// ) -> Option<gpui::Task<anyhow::Result<ViewHandle<Self>>>> {
359// let Some(proto::view::Variant::ChannelView(_)) = state else {
360// return None;
361// };
362// let Some(proto::view::Variant::ChannelView(state)) = state.take() else {
363// unreachable!()
364// };
365
366// let open = ChannelView::open_in_pane(state.channel_id, pane, workspace, cx);
367
368// Some(cx.spawn(|mut cx| async move {
369// let this = open.await?;
370
371// let task = this
372// .update(&mut cx, |this, cx| {
373// this.remote_id = Some(remote_id);
374
375// if let Some(state) = state.editor {
376// Some(this.editor.update(cx, |editor, cx| {
377// editor.apply_update_proto(
378// &this.project,
379// proto::update_view::Variant::Editor(proto::update_view::Editor {
380// selections: state.selections,
381// pending_selection: state.pending_selection,
382// scroll_top_anchor: state.scroll_top_anchor,
383// scroll_x: state.scroll_x,
384// scroll_y: state.scroll_y,
385// ..Default::default()
386// }),
387// cx,
388// )
389// }))
390// } else {
391// None
392// }
393// })
394// .ok_or_else(|| anyhow!("window was closed"))?;
395
396// if let Some(task) = task {
397// task.await?;
398// }
399
400// Ok(this)
401// }))
402// }
403
404// fn add_event_to_update_proto(
405// &self,
406// event: &Self::Event,
407// update: &mut Option<proto::update_view::Variant>,
408// cx: &AppContext,
409// ) -> bool {
410// self.editor
411// .read(cx)
412// .add_event_to_update_proto(event, update, cx)
413// }
414
415// fn apply_update_proto(
416// &mut self,
417// project: &ModelHandle<Project>,
418// message: proto::update_view::Variant,
419// cx: &mut ViewContext<Self>,
420// ) -> gpui::Task<anyhow::Result<()>> {
421// self.editor.update(cx, |editor, cx| {
422// editor.apply_update_proto(project, message, cx)
423// })
424// }
425
426// fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
427// self.editor.update(cx, |editor, cx| {
428// editor.set_leader_peer_id(leader_peer_id, cx)
429// })
430// }
431
432// fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool {
433// Editor::should_unfollow_on_event(event, cx)
434// }
435
436// fn is_project_item(&self, _cx: &AppContext) -> bool {
437// false
438// }
439// }
440
441// struct ChannelBufferCollaborationHub(ModelHandle<ChannelBuffer>);
442
443// impl CollaborationHub for ChannelBufferCollaborationHub {
444// fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
445// self.0.read(cx).collaborators()
446// }
447
448// fn user_participant_indices<'a>(
449// &self,
450// cx: &'a AppContext,
451// ) -> &'a HashMap<u64, ParticipantIndex> {
452// self.0.read(cx).user_store().read(cx).participant_indices()
453// }
454// }