Update channel moving to match

This commit is contained in:
Conrad Irwin 2024-01-26 10:07:51 -07:00
parent c6d33d4bb9
commit 08de0d88b1
6 changed files with 72 additions and 30 deletions

View file

@ -995,20 +995,20 @@ impl Database {
let new_parent = self.get_channel_internal(new_parent_id, &*tx).await?; let new_parent = self.get_channel_internal(new_parent_id, &*tx).await?;
if new_parent.root_id() != channel.root_id() { 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 if new_parent
.ancestors_including_self() .ancestors_including_self()
.any(|id| id == channel.id) .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 if channel.visibility == ChannelVisibility::Public
&& new_parent.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(); let root_id = channel.root_id();

View file

@ -1585,14 +1585,27 @@ impl CollabPanel {
cx: &mut ViewContext<CollabPanel>, cx: &mut ViewContext<CollabPanel>,
) { ) {
if let Some(clipboard) = self.channel_clipboard.take() { if let Some(clipboard) = self.channel_clipboard.take() {
self.channel_store self.move_channel(clipboard.channel_id, to_channel_id, cx)
.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)
} }
} }
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>) { fn open_channel_notes(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
if let Some(workspace) = self.workspace.upgrade() { if let Some(workspace) = self.workspace.upgrade() {
ChannelView::open(channel_id, workspace, cx).detach(); ChannelView::open(channel_id, workspace, cx).detach();
@ -2285,6 +2298,7 @@ impl CollabPanel {
}; };
let width = self.width.unwrap_or(px(240.)); let width = self.width.unwrap_or(px(240.));
let root_id = channel.root_id();
div() div()
.h_6() .h_6()
@ -2292,19 +2306,28 @@ impl CollabPanel {
.group("") .group("")
.flex() .flex()
.w_full() .w_full()
.on_drag(channel.clone(), move |channel, cx| { .when(!channel.is_root_channel(), |el| {
cx.new_view(|_| DraggedChannelView { el.on_drag(channel.clone(), move |channel, cx| {
channel: channel.clone(), cx.new_view(|_| DraggedChannelView {
width, 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| { .on_drop(cx.listener(move |this, dragged_channel: &Channel, cx| {
this.channel_store if dragged_channel.root_id() != root_id {
.update(cx, |channel_store, cx| { return;
channel_store.move_channel(dragged_channel.id, channel_id, cx) }
}) this.move_channel(dragged_channel.id, channel_id, cx);
.detach_and_prompt_err("Failed to move channel", cx, |_, _| None)
})) }))
.child( .child(
ListItem::new(channel_id as usize) ListItem::new(channel_id as usize)

View file

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

View file

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

View file

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

View file

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