channel_view.rs

  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// }