Progress

Conrad Irwin created

Change summary

crates/collab_ui2/src/collab_panel.rs | 398 +++++++++++++---------------
1 file changed, 182 insertions(+), 216 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_panel.rs 🔗

@@ -168,17 +168,17 @@ use editor::Editor;
 use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, div, img, prelude::*, serde_json, Action, AppContext, AsyncWindowContext, Div,
-    EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement, Model,
-    ParentElement, PromptLevel, Render, RenderOnce, SharedString, Styled, Subscription, Task, View,
-    ViewContext, VisualContext, WeakView,
+    actions, div, img, prelude::*, serde_json, Action, AppContext, AsyncWindowContext,
+    ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
+    IntoElement, Model, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce,
+    SharedString, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
 };
 use project::Fs;
 use serde_derive::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use ui::{
-    h_stack, v_stack, Avatar, Button, Color, Icon, IconButton, IconElement, Label, List,
-    ListHeader, ListItem, Toggle, Tooltip,
+    h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, Label,
+    List, ListHeader, ListItem, Toggle, Tooltip,
 };
 use util::{maybe, ResultExt, TryFutureExt};
 use workspace::{
@@ -230,26 +230,6 @@ pub fn init(cx: &mut AppContext) {
     //         },
     //     );
 
-    //     cx.add_action(
-    //         |panel: &mut CollabPanel,
-    //          action: &StartMoveChannelFor,
-    //          _: &mut ViewContext<CollabPanel>| {
-    //             panel.channel_clipboard = Some(ChannelMoveClipboard {
-    //                 channel_id: action.channel_id,
-    //             });
-    //         },
-    //     );
-
-    //     cx.add_action(
-    //         |panel: &mut CollabPanel, _: &StartMoveChannel, _: &mut ViewContext<CollabPanel>| {
-    //             if let Some(channel) = panel.selected_channel() {
-    //                 panel.channel_clipboard = Some(ChannelMoveClipboard {
-    //                     channel_id: channel.id,
-    //                 })
-    //             }
-    //         },
-    //     );
-
     //     cx.add_action(
     //         |panel: &mut CollabPanel, _: &MoveSelected, cx: &mut ViewContext<CollabPanel>| {
     //             let Some(clipboard) = panel.channel_clipboard.take() else {
@@ -308,7 +288,7 @@ pub struct CollabPanel {
     focus_handle: FocusHandle,
     // channel_clipboard: Option<ChannelMoveClipboard>,
     pending_serialization: Task<Option<()>>,
-    // context_menu: ViewHandle<ContextMenu>,
+    context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
     filter_editor: View<Editor>,
     channel_name_editor: View<Editor>,
     channel_editing_state: Option<ChannelEditingState>,
@@ -592,7 +572,7 @@ impl CollabPanel {
                 //                 channel_clipboard: None,
                 fs: workspace.app_state().fs.clone(),
                 pending_serialization: Task::ready(None),
-                //                 context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
+                context_menu: None,
                 channel_name_editor,
                 filter_editor,
                 entries: Vec::default(),
@@ -1695,146 +1675,120 @@ impl CollabPanel {
     //         })
     //     }
 
-    //     fn deploy_channel_context_menu(
-    //         &mut self,
-    //         position: Option<Vector2F>,
-    //         channel: &Channel,
-    //         ix: usize,
-    //         cx: &mut ViewContext<Self>,
-    //     ) {
-    //         self.context_menu_on_selected = position.is_none();
-
-    //         let clipboard_channel_name = self.channel_clipboard.as_ref().and_then(|clipboard| {
-    //             self.channel_store
-    //                 .read(cx)
-    //                 .channel_for_id(clipboard.channel_id)
-    //                 .map(|channel| channel.name.clone())
-    //         });
-
-    //         self.context_menu.update(cx, |context_menu, cx| {
-    //             context_menu.set_position_mode(if self.context_menu_on_selected {
-    //                 OverlayPositionMode::Local
-    //             } else {
-    //                 OverlayPositionMode::Window
-    //             });
-
-    //             let mut items = Vec::new();
-
-    //             let select_action_name = if self.selection == Some(ix) {
-    //                 "Unselect"
-    //             } else {
-    //                 "Select"
-    //             };
-
-    //             items.push(ContextMenuItem::action(
-    //                 select_action_name,
-    //                 ToggleSelectedIx { ix },
-    //             ));
+    fn deploy_channel_context_menu(
+        &mut self,
+        position: Point<Pixels>,
+        channel: &Channel,
+        ix: usize,
+        cx: &mut ViewContext<Self>,
+    ) {
+        // self.context_menu_on_selected = position.is_none();
+
+        // let clipboard_channel_name = self.channel_clipboard.as_ref().and_then(|clipboard| {
+        //     self.channel_store
+        //         .read(cx)
+        //         .channel_for_id(clipboard.channel_id)
+        //         .map(|channel| channel.name.clone())
+        // });
+        let this = cx.view();
+        let has_subchannels = self.has_subchannels(ix);
+        let is_channel_collapsed = self.is_channel_collapsed(channel_id);
+
+        let menu = ContextMenu::build(cx, |context_menu, cx| {
+            if has_subchannels {
+                let expand_action_name = if is_channel_collapsed {
+                    "Expand Subchannels"
+                } else {
+                    "Collapse Subchannels"
+                };
+                context_menu = context_menu.entry(
+                    expand_action_name,
+                    cx.handler_for(&this, |this, cx| {
+                        this.toggle_channel_collapsed(channel.id, cx)
+                    }),
+                );
+            }
 
-    //             if self.has_subchannels(ix) {
-    //                 let expand_action_name = if self.is_channel_collapsed(channel.id) {
-    //                     "Expand Subchannels"
-    //                 } else {
-    //                     "Collapse Subchannels"
-    //                 };
-    //                 items.push(ContextMenuItem::action(
-    //                     expand_action_name,
-    //                     ToggleCollapse {
-    //                         location: channel.id,
-    //                     },
-    //                 ));
-    //             }
+            context_menu = context_menu
+                .entry(
+                    "Open Notes",
+                    cx.handler_for(&this, |this, cx| this.open_channel_notes(channel.id, cx)),
+                )
+                .entry(
+                    "Open Chat",
+                    cx.handler_for(&this, |this, cx| this.join_channel_chat(channel.id, cx)),
+                )
+                .entry(
+                    "Copy Channel Link",
+                    cx.handler_for(&this, |this, cx| this.copy_channel_link(channel.id, cx)),
+                );
 
-    //             items.push(ContextMenuItem::action(
-    //                 "Open Notes",
-    //                 OpenChannelNotes {
-    //                     channel_id: channel.id,
-    //                 },
-    //             ));
+            if self.channel_store.read(cx).is_channel_admin(channel.id) {
+                context_menu = context_menu
+                    .separator()
+                    .entry(
+                        "New Subchannel",
+                        cx.handler_for(&this, |this, cx| this.new_subchannel(channel.id, cx)),
+                    )
+                    .entry(
+                        "Rename",
+                        cx.handler_for(&this, |this, cx| this.rename_channel(channel.id, cx)),
+                    )
+                    .entry(
+                        "Move this channel",
+                        cx.handler_for(&this, |this, cx| this.start_move_channel(channel.id, cx)),
+                    );
+
+                // if let Some(channel_name) = clipboard_channel_name {
+                //     items.push(ContextMenuItem::Separator);
+                //     items.push(ContextMenuItem::action(
+                //         format!("Move '#{}' here", channel_name),
+                //         MoveChannel { to: channel.id },
+                //     ));
+                // }
 
-    //             items.push(ContextMenuItem::action(
-    //                 "Open Chat",
-    //                 JoinChannelChat {
-    //                     channel_id: channel.id,
-    //                 },
-    //             ));
+                // items.extend([
+                //     ContextMenuItem::Separator,
+                //     ContextMenuItem::action(
+                //         "Invite Members",
+                //         InviteMembers {
+                //             channel_id: channel.id,
+                //         },
+                //     ),
+                //     ContextMenuItem::action(
+                //         "Manage Members",
+                //         ManageMembers {
+                //             channel_id: channel.id,
+                //         },
+                //     ),
+                //     ContextMenuItem::Separator,
+                //     ContextMenuItem::action(
+                //         "Delete",
+                //         RemoveChannel {
+                //             channel_id: channel.id,
+                //         },
+                //     ),
+                // ]);
+            }
 
-    //             items.push(ContextMenuItem::action(
-    //                 "Copy Channel Link",
-    //                 CopyChannelLink {
-    //                     channel_id: channel.id,
-    //                 },
-    //             ));
-
-    //             if self.channel_store.read(cx).is_channel_admin(channel.id) {
-    //                 items.extend([
-    //                     ContextMenuItem::Separator,
-    //                     ContextMenuItem::action(
-    //                         "New Subchannel",
-    //                         NewChannel {
-    //                             location: channel.id,
-    //                         },
-    //                     ),
-    //                     ContextMenuItem::action(
-    //                         "Rename",
-    //                         RenameChannel {
-    //                             channel_id: channel.id,
-    //                         },
-    //                     ),
-    //                     ContextMenuItem::action(
-    //                         "Move this channel",
-    //                         StartMoveChannelFor {
-    //                             channel_id: channel.id,
-    //                         },
-    //                     ),
-    //                 ]);
-
-    //                 if let Some(channel_name) = clipboard_channel_name {
-    //                     items.push(ContextMenuItem::Separator);
-    //                     items.push(ContextMenuItem::action(
-    //                         format!("Move '#{}' here", channel_name),
-    //                         MoveChannel { to: channel.id },
-    //                     ));
-    //                 }
+            // context_menu.show(
+            //     position.unwrap_or_default(),
+            //     if self.context_menu_on_selected {
+            //         gpui::elements::AnchorCorner::TopRight
+            //     } else {
+            //         gpui::elements::AnchorCorner::BottomLeft
+            //     },
+            //     items,
+            //     cx,
+            // );
 
-    //                 items.extend([
-    //                     ContextMenuItem::Separator,
-    //                     ContextMenuItem::action(
-    //                         "Invite Members",
-    //                         InviteMembers {
-    //                             channel_id: channel.id,
-    //                         },
-    //                     ),
-    //                     ContextMenuItem::action(
-    //                         "Manage Members",
-    //                         ManageMembers {
-    //                             channel_id: channel.id,
-    //                         },
-    //                     ),
-    //                     ContextMenuItem::Separator,
-    //                     ContextMenuItem::action(
-    //                         "Delete",
-    //                         RemoveChannel {
-    //                             channel_id: channel.id,
-    //                         },
-    //                     ),
-    //                 ]);
-    //             }
+            context_menu
+        });
 
-    //             context_menu.show(
-    //                 position.unwrap_or_default(),
-    //                 if self.context_menu_on_selected {
-    //                     gpui::elements::AnchorCorner::TopRight
-    //                 } else {
-    //                     gpui::elements::AnchorCorner::BottomLeft
-    //                 },
-    //                 items,
-    //                 cx,
-    //             );
-    //         });
+        self.context_menu = Some((menu, (), ()));
 
-    //         cx.notify();
-    //     }
+        cx.notify();
+    }
 
     //     fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
     //         if self.take_editing_state(cx) {
@@ -2116,18 +2070,18 @@ impl CollabPanel {
         });
     }
 
-    //     fn new_subchannel(&mut self, action: &NewChannel, cx: &mut ViewContext<Self>) {
-    //         self.collapsed_channels
-    //             .retain(|channel| *channel != action.location);
-    //         self.channel_editing_state = Some(ChannelEditingState::Create {
-    //             location: Some(action.location.to_owned()),
-    //             pending_name: None,
-    //         });
-    //         self.update_entries(false, cx);
-    //         self.select_channel_editor();
-    //         cx.focus(self.channel_name_editor.as_any());
-    //         cx.notify();
-    //     }
+    fn new_subchannel(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+        self.collapsed_channels
+            .retain(|channel| *channel != channel_id);
+        self.channel_editing_state = Some(ChannelEditingState::Create {
+            location: Some(action.location.to_owned()),
+            pending_name: None,
+        });
+        self.update_entries(false, cx);
+        self.select_channel_editor();
+        cx.focus(self.channel_name_editor.as_any());
+        cx.notify();
+    }
 
     //     fn invite_members(&mut self, action: &InviteMembers, cx: &mut ViewContext<Self>) {
     //         self.show_channel_modal(action.channel_id, channel_modal::Mode::InviteMembers, cx);
@@ -2154,25 +2108,37 @@ impl CollabPanel {
     //         }
     //     }
 
-    //     fn rename_channel(&mut self, action: &RenameChannel, cx: &mut ViewContext<Self>) {
-    //         let channel_store = self.channel_store.read(cx);
-    //         if !channel_store.is_channel_admin(action.channel_id) {
-    //             return;
-    //         }
-    //         if let Some(channel) = channel_store.channel_for_id(action.channel_id).cloned() {
-    //             self.channel_editing_state = Some(ChannelEditingState::Rename {
-    //                 location: action.channel_id.to_owned(),
-    //                 pending_name: None,
-    //             });
-    //             self.channel_name_editor.update(cx, |editor, cx| {
-    //                 editor.set_text(channel.name.clone(), cx);
-    //                 editor.select_all(&Default::default(), cx);
-    //             });
-    //             cx.focus(self.channel_name_editor.as_any());
-    //             self.update_entries(false, cx);
-    //             self.select_channel_editor();
-    //         }
-    //     }
+    fn rename_channel(&mut self, action: &RenameChannel, cx: &mut ViewContext<Self>) {
+        let channel_store = self.channel_store.read(cx);
+        if !channel_store.is_channel_admin(action.channel_id) {
+            return;
+        }
+        if let Some(channel) = channel_store.channel_for_id(action.channel_id).cloned() {
+            self.channel_editing_state = Some(ChannelEditingState::Rename {
+                location: action.channel_id.to_owned(),
+                pending_name: None,
+            });
+            self.channel_name_editor.update(cx, |editor, cx| {
+                editor.set_text(channel.name.clone(), cx);
+                editor.select_all(&Default::default(), cx);
+            });
+            cx.focus(self.channel_name_editor.as_any());
+            self.update_entries(false, cx);
+            self.select_channel_editor();
+        }
+    }
+
+    fn start_move_channel(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+        self.channel_clipboard = Some(ChannelMoveClipboard { channel_id });
+    }
+
+    fn start_move_selected_channel(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+        if let Some(channel) = self.selected_channel() {
+            self.channel_clipboard = Some(ChannelMoveClipboard {
+                channel_id: channel.id,
+            })
+        }
+    }
 
     fn open_channel_notes(&mut self, action: &OpenChannelNotes, cx: &mut ViewContext<Self>) {
         if let Some(workspace) = self.workspace.upgrade() {
@@ -2344,31 +2310,31 @@ impl CollabPanel {
         .detach()
     }
 
-    //     fn join_channel_chat(&mut self, action: &JoinChannelChat, cx: &mut ViewContext<Self>) {
-    //         let channel_id = action.channel_id;
-    //         if let Some(workspace) = self.workspace.upgrade(cx) {
-    //             cx.app_context().defer(move |cx| {
-    //                 workspace.update(cx, |workspace, cx| {
-    //                     if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
-    //                         panel.update(cx, |panel, cx| {
-    //                             panel
-    //                                 .select_channel(channel_id, None, cx)
-    //                                 .detach_and_log_err(cx);
-    //                         });
-    //                     }
-    //                 });
-    //             });
-    //         }
-    //     }
+    fn join_channel_chat(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+        let Some(workspace) = self.workspace.upgrade() else {
+            return;
+        };
+        cx.defer(move |cx| {
+            workspace.update(cx, |workspace, cx| {
+                if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
+                    panel.update(cx, |panel, cx| {
+                        panel
+                            .select_channel(channel_id, None, cx)
+                            .detach_and_log_err(cx);
+                    });
+                }
+            });
+        });
+    }
 
-    //     fn copy_channel_link(&mut self, action: &CopyChannelLink, cx: &mut ViewContext<Self>) {
-    //         let channel_store = self.channel_store.read(cx);
-    //         let Some(channel) = channel_store.channel_for_id(action.channel_id) else {
-    //             return;
-    //         };
-    //         let item = ClipboardItem::new(channel.link());
-    //         cx.write_to_clipboard(item)
-    //     }
+    fn copy_channel_link(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+        let channel_store = self.channel_store.read(cx);
+        let Some(channel) = channel_store.channel_for_id(channel_id) else {
+            return;
+        };
+        let item = ClipboardItem::new(channel.link());
+        cx.write_to_clipboard(item)
+    }
 
     fn render_signed_out(&mut self, cx: &mut ViewContext<Self>) -> Div {
         v_stack().child(Button::new("Sign in to collaborate").on_click(cx.listener(