Update channel moving to match

Conrad Irwin created

Change summary

crates/collab/src/db/queries/channels.rs  |  6 +-
crates/collab_ui/src/collab_panel.rs      | 53 +++++++++++++++++-------
crates/gpui/src/elements/div.rs           | 27 +++++++++---
crates/project_panel/src/project_panel.rs |  2 
crates/rpc/proto/zed.proto                |  2 
crates/workspace/src/pane.rs              | 12 +++-
6 files changed, 72 insertions(+), 30 deletions(-)

Detailed changes

crates/collab/src/db/queries/channels.rs 🔗

@@ -995,20 +995,20 @@ impl Database {
             let new_parent = self.get_channel_internal(new_parent_id, &*tx).await?;
 
             if new_parent.root_id() != channel.root_id() {
-                Err(anyhow!("cannot move a channel into a different root"))?;
+                Err(anyhow!(ErrorCode::WrongMoveTarget))?;
             }
 
             if new_parent
                 .ancestors_including_self()
                 .any(|id| id == channel.id)
             {
-                Err(anyhow!("cannot move a channel into one of its descendants"))?;
+                Err(anyhow!(ErrorCode::CircularNesting))?;
             }
 
             if channel.visibility == ChannelVisibility::Public
                 && new_parent.visibility != ChannelVisibility::Public
             {
-                Err(anyhow!("public channels must descend from public channels"))?;
+                Err(anyhow!(ErrorCode::BadPublicNesting))?;
             }
 
             let root_id = channel.root_id();

crates/collab_ui/src/collab_panel.rs 🔗

@@ -1585,14 +1585,27 @@ impl CollabPanel {
         cx: &mut ViewContext<CollabPanel>,
     ) {
         if let Some(clipboard) = self.channel_clipboard.take() {
-            self.channel_store
-                .update(cx, |channel_store, cx| {
-                    channel_store.move_channel(clipboard.channel_id, to_channel_id, cx)
-                })
-                .detach_and_prompt_err("Failed to move channel", cx, |_, _| None)
+            self.move_channel(clipboard.channel_id, to_channel_id, cx)
         }
     }
 
+    fn move_channel(&self, channel_id: ChannelId, to: ChannelId, cx: &mut ViewContext<Self>) {
+        self.channel_store
+            .update(cx, |channel_store, cx| {
+                channel_store.move_channel(channel_id, to, cx)
+            })
+            .detach_and_prompt_err("Failed to move channel", cx, |e, _| match e.error_code() {
+                ErrorCode::BadPublicNesting => {
+                    Some("Public channels must have public parents".into())
+                }
+                ErrorCode::CircularNesting => Some("You cannot move a channel into itself".into()),
+                ErrorCode::WrongMoveTarget => {
+                    Some("You cannot move a channel into a different root channel".into())
+                }
+                _ => None,
+            })
+    }
+
     fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
         if let Some(workspace) = self.workspace.upgrade() {
             ChannelView::open(channel_id, workspace, cx).detach();
@@ -2285,6 +2298,7 @@ impl CollabPanel {
         };
 
         let width = self.width.unwrap_or(px(240.));
+        let root_id = channel.root_id();
 
         div()
             .h_6()
@@ -2292,19 +2306,28 @@ impl CollabPanel {
             .group("")
             .flex()
             .w_full()
-            .on_drag(channel.clone(), move |channel, cx| {
-                cx.new_view(|_| DraggedChannelView {
-                    channel: channel.clone(),
-                    width,
+            .when(!channel.is_root_channel(), |el| {
+                el.on_drag(channel.clone(), move |channel, cx| {
+                    cx.new_view(|_| DraggedChannelView {
+                        channel: channel.clone(),
+                        width,
+                    })
                 })
             })
-            .drag_over::<Channel>(|style| style.bg(cx.theme().colors().ghost_element_hover))
+            .drag_over::<Channel>({
+                move |style, dragged_channel: &Channel, cx| {
+                    if dragged_channel.root_id() == root_id {
+                        style.bg(cx.theme().colors().ghost_element_hover)
+                    } else {
+                        style
+                    }
+                }
+            })
             .on_drop(cx.listener(move |this, dragged_channel: &Channel, cx| {
-                this.channel_store
-                    .update(cx, |channel_store, cx| {
-                        channel_store.move_channel(dragged_channel.id, channel_id, cx)
-                    })
-                    .detach_and_prompt_err("Failed to move channel", cx, |_, _| None)
+                if dragged_channel.root_id() != root_id {
+                    return;
+                }
+                this.move_channel(dragged_channel.id, channel_id, cx);
             }))
             .child(
                 ListItem::new(channel_id as usize)

crates/gpui/src/elements/div.rs 🔗

@@ -782,10 +782,20 @@ pub trait InteractiveElement: Sized {
     }
 
     /// Apply the given style when the given data type is dragged over this element
-    fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
-        self.interactivity()
-            .drag_over_styles
-            .push((TypeId::of::<S>(), f(StyleRefinement::default())));
+    fn drag_over<S: 'static>(
+        mut self,
+        f: impl 'static + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
+    ) -> Self {
+        self.interactivity().drag_over_styles.push((
+            TypeId::of::<S>(),
+            Box::new(move |currently_dragged: &dyn Any, cx| {
+                f(
+                    StyleRefinement::default(),
+                    currently_dragged.downcast_ref::<S>().unwrap(),
+                    cx,
+                )
+            }),
+        ));
         self
     }
 
@@ -1174,7 +1184,10 @@ pub struct Interactivity {
     pub(crate) group_hover_style: Option<GroupStyle>,
     pub(crate) active_style: Option<Box<StyleRefinement>>,
     pub(crate) group_active_style: Option<GroupStyle>,
-    pub(crate) drag_over_styles: Vec<(TypeId, StyleRefinement)>,
+    pub(crate) drag_over_styles: Vec<(
+        TypeId,
+        Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
+    )>,
     pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
     pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
     pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
@@ -1980,7 +1993,7 @@ impl Interactivity {
                             }
                         }
 
-                        for (state_type, drag_over_style) in &self.drag_over_styles {
+                        for (state_type, build_drag_over_style) in &self.drag_over_styles {
                             if *state_type == drag.value.as_ref().type_id()
                                 && bounds
                                     .intersect(&cx.content_mask().bounds)
@@ -1990,7 +2003,7 @@ impl Interactivity {
                                     cx.stacking_order(),
                                 )
                             {
-                                style.refine(drag_over_style);
+                                style.refine(&build_drag_over_style(drag.value.as_ref(), cx));
                             }
                         }
                     }

crates/project_panel/src/project_panel.rs 🔗

@@ -1368,7 +1368,7 @@ impl ProjectPanel {
                     entry_id: *entry_id,
                 })
             })
-            .drag_over::<ProjectEntryId>(|style| {
+            .drag_over::<ProjectEntryId>(|style, _, cx| {
                 style.bg(cx.theme().colors().drop_target_background)
             })
             .on_drop(cx.listener(move |this, dragged_id: &ProjectEntryId, cx| {

crates/rpc/proto/zed.proto 🔗

@@ -214,6 +214,8 @@ enum ErrorCode {
     NeedsCla = 7;
     NotARootChannel = 8;
     BadPublicNesting = 9;
+    CircularNesting = 10;
+    WrongMoveTarget = 11;
 }
 
 message Test {

crates/workspace/src/pane.rs 🔗

@@ -1334,8 +1334,12 @@ impl Pane {
                 },
                 |tab, cx| cx.new_view(|_| tab.clone()),
             )
-            .drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().drop_target_background))
-            .drag_over::<ProjectEntryId>(|tab| tab.bg(cx.theme().colors().drop_target_background))
+            .drag_over::<DraggedTab>(|tab, _, cx| {
+                tab.bg(cx.theme().colors().drop_target_background)
+            })
+            .drag_over::<ProjectEntryId>(|tab, _, cx| {
+                tab.bg(cx.theme().colors().drop_target_background)
+            })
             .when_some(self.can_drop_predicate.clone(), |this, p| {
                 this.can_drop(move |a, cx| p(a, cx))
             })
@@ -1505,10 +1509,10 @@ impl Pane {
                     .child("")
                     .h_full()
                     .flex_grow()
-                    .drag_over::<DraggedTab>(|bar| {
+                    .drag_over::<DraggedTab>(|bar, _, cx| {
                         bar.bg(cx.theme().colors().drop_target_background)
                     })
-                    .drag_over::<ProjectEntryId>(|bar| {
+                    .drag_over::<ProjectEntryId>(|bar, _, cx| {
                         bar.bg(cx.theme().colors().drop_target_background)
                     })
                     .on_drop(cx.listener(move |this, dragged_tab: &DraggedTab, cx| {