Render channel invites in collab panel, fix unused warnings

Max Brunsfeld created

Change summary

crates/collab_ui2/src/collab_panel.rs                | 818 ++-----------
crates/collab_ui2/src/collab_panel/channel_modal.rs  |   1 
crates/collab_ui2/src/collab_panel/contact_finder.rs |   1 
3 files changed, 139 insertions(+), 681 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_panel.rs 🔗

@@ -1,4 +1,3 @@
-#![allow(unused)]
 mod channel_modal;
 mod contact_finder;
 
@@ -317,18 +316,10 @@ pub struct CollabPanel {
     subscriptions: Vec<Subscription>,
     collapsed_sections: Vec<Section>,
     collapsed_channels: Vec<ChannelId>,
-    drag_target_channel: ChannelDragTarget,
     workspace: WeakView<Workspace>,
     // context_menu_on_selected: bool,
 }
 
-#[derive(PartialEq, Eq)]
-enum ChannelDragTarget {
-    None,
-    Root,
-    Channel(ChannelId),
-}
-
 #[derive(Serialize, Deserialize)]
 struct SerializedCollabPanel {
     width: Option<Pixels>,
@@ -373,7 +364,7 @@ enum ListEntry {
     },
     IncomingRequest(Arc<User>),
     OutgoingRequest(Arc<User>),
-    //     ChannelInvite(Arc<Channel>),
+    ChannelInvite(Arc<Channel>),
     Channel {
         channel: Arc<Channel>,
         depth: usize,
@@ -472,8 +463,6 @@ impl CollabPanel {
                 collapsed_channels: Vec::default(),
                 workspace: workspace.weak_handle(),
                 client: workspace.app_state().client.clone(),
-                //                 context_menu_on_selected: true,
-                drag_target_channel: ChannelDragTarget::None,
             };
 
             this.update_entries(false, cx);
@@ -529,9 +518,6 @@ impl CollabPanel {
         })
     }
 
-    fn contacts(&self, cx: &AppContext) -> Option<Vec<Arc<Contact>>> {
-        Some(self.user_store.read(cx).contacts().to_owned())
-    }
     pub async fn load(
         workspace: WeakView<Workspace>,
         mut cx: AsyncWindowContext,
@@ -808,37 +794,37 @@ impl CollabPanel {
                 }
             }
 
-            //             let channel_invites = channel_store.channel_invitations();
-            //             if !channel_invites.is_empty() {
-            //                 self.match_candidates.clear();
-            //                 self.match_candidates
-            //                     .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
-            //                         StringMatchCandidate {
-            //                             id: ix,
-            //                             string: channel.name.clone(),
-            //                             char_bag: channel.name.chars().collect(),
-            //                         }
-            //                     }));
-            //                 let matches = executor.block(match_strings(
-            //                     &self.match_candidates,
-            //                     &query,
-            //                     true,
-            //                     usize::MAX,
-            //                     &Default::default(),
-            //                     executor.clone(),
-            //                 ));
-            //                 request_entries.extend(matches.iter().map(|mat| {
-            //                     ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
-            //                 }));
-
-            //                 if !request_entries.is_empty() {
-            //                     self.entries
-            //                         .push(ListEntry::Header(Section::ChannelInvites));
-            //                     if !self.collapsed_sections.contains(&Section::ChannelInvites) {
-            //                         self.entries.append(&mut request_entries);
-            //                     }
-            //                 }
-            //             }
+            let channel_invites = channel_store.channel_invitations();
+            if !channel_invites.is_empty() {
+                self.match_candidates.clear();
+                self.match_candidates
+                    .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
+                        StringMatchCandidate {
+                            id: ix,
+                            string: channel.name.clone().into(),
+                            char_bag: channel.name.chars().collect(),
+                        }
+                    }));
+                let matches = executor.block(match_strings(
+                    &self.match_candidates,
+                    &query,
+                    true,
+                    usize::MAX,
+                    &Default::default(),
+                    executor.clone(),
+                ));
+                request_entries.extend(matches.iter().map(|mat| {
+                    ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
+                }));
+
+                if !request_entries.is_empty() {
+                    self.entries
+                        .push(ListEntry::Header(Section::ChannelInvites));
+                    if !self.collapsed_sections.contains(&Section::ChannelInvites) {
+                        self.entries.append(&mut request_entries);
+                    }
+                }
+            }
         }
 
         self.entries.push(ListEntry::Header(Section::Contacts));
@@ -1049,9 +1035,7 @@ impl CollabPanel {
             } else if is_current_user {
                 IconButton::new("leave-call", Icon::Exit)
                     .style(ButtonStyle::Subtle)
-                    .on_click(cx.listener(move |this, _, cx| {
-                        Self::leave_call(cx);
-                    }))
+                    .on_click(move |_, cx| Self::leave_call(cx))
                     .tooltip(|cx| Tooltip::text("Leave Call", cx))
                     .into_any_element()
             } else {
@@ -1061,7 +1045,8 @@ impl CollabPanel {
                 this.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
                     .on_click(cx.listener(move |this, _, cx| {
                         this.workspace
-                            .update(cx, |workspace, cx| workspace.follow(peer_id, cx));
+                            .update(cx, |workspace, cx| workspace.follow(peer_id, cx))
+                            .ok();
                     }))
             })
     }
@@ -1073,7 +1058,7 @@ impl CollabPanel {
         host_user_id: u64,
         //         is_current: bool,
         is_last: bool,
-        //         is_selected: bool,
+        is_selected: bool,
         //         theme: &theme::Theme,
         cx: &mut ViewContext<Self>,
     ) -> impl IntoElement {
@@ -1084,15 +1069,16 @@ impl CollabPanel {
         }
         .into();
 
-        let theme = cx.theme();
-
         ListItem::new(project_id as usize)
+            .selected(is_selected)
             .on_click(cx.listener(move |this, _, cx| {
-                this.workspace.update(cx, |workspace, cx| {
-                    let app_state = workspace.app_state().clone();
-                    workspace::join_remote_project(project_id, host_user_id, app_state, cx)
-                        .detach_and_log_err(cx);
-                });
+                this.workspace
+                    .update(cx, |workspace, cx| {
+                        let app_state = workspace.app_state().clone();
+                        workspace::join_remote_project(project_id, host_user_id, app_state, cx)
+                            .detach_and_log_err(cx);
+                    })
+                    .ok();
             }))
             .start_slot(
                 h_stack()
@@ -1102,95 +1088,19 @@ impl CollabPanel {
             )
             .child(Label::new(project_name.clone()))
             .tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx))
-
-        //         enum JoinProject {}
-        //         enum JoinProjectTooltip {}
-
-        //         let collab_theme = &theme.collab_panel;
-        //         let host_avatar_width = collab_theme
-        //             .contact_avatar
-        //             .width
-        //             .or(collab_theme.contact_avatar.height)
-        //             .unwrap_or(0.);
-        //         let tree_branch = collab_theme.tree_branch;
-
-        //         let content =
-        //             MouseEventHandler::new::<JoinProject, _>(project_id as usize, cx, |mouse_state, cx| {
-        //                 let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
-        //                 let row = if is_current {
-        //                     collab_theme
-        //                         .project_row
-        //                         .in_state(true)
-        //                         .style_for(&mut Default::default())
-        //                 } else {
-        //                     collab_theme
-        //                         .project_row
-        //                         .in_state(is_selected)
-        //                         .style_for(mouse_state)
-        //                 };
-
-        //                 Flex::row()
-        //                     .with_child(render_tree_branch(
-        //                         tree_branch,
-        //                         &row.name.text,
-        //                         is_last,
-        //                         vec2f(host_avatar_width, collab_theme.row_height),
-        //                         cx.font_cache(),
-        //                     ))
-        //                     .with_child(
-        //                         Svg::new("icons/file_icons/folder.svg")
-        //                             .with_color(collab_theme.channel_hash.color)
-        //                             .constrained()
-        //                             .with_width(collab_theme.channel_hash.width)
-        //                             .aligned()
-        //                             .left(),
-        //                     )
-        //                     .with_child(
-        //                         Label::new(project_name.clone(), row.name.text.clone())
-        //                             .aligned()
-        //                             .left()
-        //                             .contained()
-        //                             .with_style(row.name.container)
-        //                             .flex(1., false),
-        //                     )
-        //                     .constrained()
-        //                     .with_height(collab_theme.row_height)
-        //                     .contained()
-        //                     .with_style(row.container)
-        //             });
-
-        //         if is_current {
-        //             return content.into_any();
-        //         }
-
-        //         content
-        //             .with_cursor_style(CursorStyle::PointingHand)
-        //             .on_click(MouseButton::Left, move |_, this, cx| {
-        //                 if let Some(workspace) = this.workspace.upgrade(cx) {
-        //                     let app_state = workspace.read(cx).app_state().clone();
-        //                     workspace::join_remote_project(project_id, host_user_id, app_state, cx)
-        //                         .detach_and_log_err(cx);
-        //                 }
-        //             })
-        //             .with_tooltip::<JoinProjectTooltip>(
-        //                 project_id as usize,
-        //                 format!("Open {}", project_name),
-        //                 None,
-        //                 theme.tooltip.clone(),
-        //                 cx,
-        //             )
-        //             .into_any()
     }
 
     fn render_participant_screen(
         &self,
         peer_id: Option<PeerId>,
         is_last: bool,
+        is_selected: bool,
         cx: &mut ViewContext<Self>,
     ) -> impl IntoElement {
         let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
 
         ListItem::new(("screen", id))
+            .selected(is_selected)
             .start_slot(
                 h_stack()
                     .gap_1()
@@ -1200,9 +1110,11 @@ impl CollabPanel {
             .child(Label::new("Screen"))
             .when_some(peer_id, |this, _| {
                 this.on_click(cx.listener(move |this, _, cx| {
-                    this.workspace.update(cx, |workspace, cx| {
-                        workspace.open_shared_screen(peer_id.unwrap(), cx)
-                    });
+                    this.workspace
+                        .update(cx, |workspace, cx| {
+                            workspace.open_shared_screen(peer_id.unwrap(), cx)
+                        })
+                        .ok();
                 }))
                 .tooltip(move |cx| Tooltip::text(format!("Open shared screen"), cx))
             })
@@ -1291,86 +1203,6 @@ impl CollabPanel {
             .tooltip(move |cx| Tooltip::text("Open Chat", cx))
     }
 
-    //     fn render_channel_invite(
-    //         channel: Arc<Channel>,
-    //         channel_store: ModelHandle<ChannelStore>,
-    //         theme: &theme::CollabPanel,
-    //         is_selected: bool,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> AnyElement<Self> {
-    //         enum Decline {}
-    //         enum Accept {}
-
-    //         let channel_id = channel.id;
-    //         let is_invite_pending = channel_store
-    //             .read(cx)
-    //             .has_pending_channel_invite_response(&channel);
-    //         let button_spacing = theme.contact_button_spacing;
-
-    //         Flex::row()
-    //             .with_child(
-    //                 Svg::new("icons/hash.svg")
-    //                     .with_color(theme.channel_hash.color)
-    //                     .constrained()
-    //                     .with_width(theme.channel_hash.width)
-    //                     .aligned()
-    //                     .left(),
-    //             )
-    //             .with_child(
-    //                 Label::new(channel.name.clone(), theme.contact_username.text.clone())
-    //                     .contained()
-    //                     .with_style(theme.contact_username.container)
-    //                     .aligned()
-    //                     .left()
-    //                     .flex(1., true),
-    //             )
-    //             .with_child(
-    //                 MouseEventHandler::new::<Decline, _>(channel.id as usize, cx, |mouse_state, _| {
-    //                     let button_style = if is_invite_pending {
-    //                         &theme.disabled_button
-    //                     } else {
-    //                         theme.contact_button.style_for(mouse_state)
-    //                     };
-    //                     render_icon_button(button_style, "icons/x.svg").aligned()
-    //                 })
-    //                 .with_cursor_style(CursorStyle::PointingHand)
-    //                 .on_click(MouseButton::Left, move |_, this, cx| {
-    //                     this.respond_to_channel_invite(channel_id, false, cx);
-    //                 })
-    //                 .contained()
-    //                 .with_margin_right(button_spacing),
-    //             )
-    //             .with_child(
-    //                 MouseEventHandler::new::<Accept, _>(channel.id as usize, cx, |mouse_state, _| {
-    //                     let button_style = if is_invite_pending {
-    //                         &theme.disabled_button
-    //                     } else {
-    //                         theme.contact_button.style_for(mouse_state)
-    //                     };
-    //                     render_icon_button(button_style, "icons/check.svg")
-    //                         .aligned()
-    //                         .flex_float()
-    //                 })
-    //                 .with_cursor_style(CursorStyle::PointingHand)
-    //                 .on_click(MouseButton::Left, move |_, this, cx| {
-    //                     this.respond_to_channel_invite(channel_id, true, cx);
-    //                 }),
-    //             )
-    //             .constrained()
-    //             .with_height(theme.row_height)
-    //             .contained()
-    //             .with_style(
-    //                 *theme
-    //                     .contact_row
-    //                     .in_state(is_selected)
-    //                     .style_for(&mut Default::default()),
-    //             )
-    //             .with_padding_left(
-    //                 theme.contact_row.default_style().padding.left + theme.channel_indent,
-    //             )
-    //             .into_any()
-    //     }
-
     fn has_subchannels(&self, ix: usize) -> bool {
         self.entries.get(ix).map_or(false, |entry| {
             if let ListEntry::Channel { has_children, .. } = entry {
@@ -1724,7 +1556,7 @@ impl CollabPanel {
         self.collapsed_channels.binary_search(&channel_id).is_ok()
     }
 
-    fn leave_call(cx: &mut ViewContext<Self>) {
+    fn leave_call(cx: &mut WindowContext) {
         ActiveCall::global(cx)
             .update(cx, |call, cx| call.hang_up(cx))
             .detach_and_log_err(cx);
@@ -1813,15 +1645,13 @@ impl CollabPanel {
         }
     }
 
-    fn start_move_channel(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
+    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>) {
+    fn start_move_selected_channel(&mut self, _: &StartMoveChannel, cx: &mut ViewContext<Self>) {
         if let Some(channel) = self.selected_channel() {
-            self.channel_clipboard = Some(ChannelMoveClipboard {
-                channel_id: channel.id,
-            })
+            self.start_move_channel(channel.id, cx);
         }
     }
 
@@ -1900,10 +1730,6 @@ impl CollabPanel {
         .detach();
     }
 
-    //     fn remove_selected_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
-    //         self.remove_channel(action.channel_id, cx)
-    //     }
-
     fn remove_channel(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
         let channel_store = self.channel_store.clone();
         if let Some(channel) = channel_store.read(cx).channel_for_id(channel_id) {
@@ -1911,9 +1737,7 @@ impl CollabPanel {
                 "Are you sure you want to remove the channel \"{}\"?",
                 channel.name
             );
-            let mut answer =
-                cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
-            let window = cx.window();
+            let answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
             cx.spawn(|this, mut cx| async move {
                 if answer.await? == 0 {
                     channel_store
@@ -1937,8 +1761,7 @@ impl CollabPanel {
             "Are you sure you want to remove \"{}\" from your contacts?",
             github_login
         );
-        let mut answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
-        let window = cx.window();
+        let answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
         cx.spawn(|_, mut cx| async move {
             if answer.await? == 0 {
                 user_store
@@ -1964,18 +1787,18 @@ impl CollabPanel {
             .detach_and_log_err(cx);
     }
 
-    //     fn respond_to_channel_invite(
-    //         &mut self,
-    //         channel_id: u64,
-    //         accept: bool,
-    //         cx: &mut ViewContext<Self>,
-    //     ) {
-    //         self.channel_store
-    //             .update(cx, |store, cx| {
-    //                 store.respond_to_channel_invite(channel_id, accept, cx)
-    //             })
-    //             .detach();
-    //     }
+    fn respond_to_channel_invite(
+        &mut self,
+        channel_id: u64,
+        accept: bool,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.channel_store
+            .update(cx, |store, cx| {
+                store.respond_to_channel_invite(channel_id, accept, cx)
+            })
+            .detach();
+    }
 
     fn call(&mut self, recipient_user_id: u64, cx: &mut ViewContext<Self>) {
         ActiveCall::global(cx)
@@ -2096,6 +1919,9 @@ impl CollabPanel {
             ListEntry::ChannelEditor { depth } => {
                 self.render_channel_editor(*depth, cx).into_any_element()
             }
+            ListEntry::ChannelInvite(channel) => self
+                .render_channel_invite(channel, is_selected, cx)
+                .into_any_element(),
             ListEntry::CallParticipant {
                 user,
                 peer_id,
@@ -2114,11 +1940,12 @@ impl CollabPanel {
                     &worktree_root_names,
                     *host_user_id,
                     *is_last,
+                    is_selected,
                     cx,
                 )
                 .into_any_element(),
             ListEntry::ParticipantScreen { peer_id, is_last } => self
-                .render_participant_screen(*peer_id, *is_last, cx)
+                .render_participant_screen(*peer_id, *is_last, is_selected, cx)
                 .into_any_element(),
             ListEntry::ChannelNotes { channel_id } => self
                 .render_channel_notes(*channel_id, cx)
@@ -2233,7 +2060,7 @@ impl CollabPanel {
                 ListHeader::new(text)
                     .when(can_collapse, |header| {
                         header.toggle(Some(!is_collapsed)).on_toggle(cx.listener(
-                            move |this, event, cx| {
+                            move |this, _, cx| {
                                 this.toggle_section_expanded(section, cx);
                             },
                         ))
@@ -2265,8 +2092,9 @@ impl CollabPanel {
         let busy = contact.busy || calling;
         let user_id = contact.user.id;
         let github_login = SharedString::from(contact.user.github_login.clone());
-        let mut item =
+        let item =
             ListItem::new(github_login.clone())
+                .selected(is_selected)
                 .on_click(cx.listener(move |this, _, cx| this.call(user_id, cx)))
                 .child(
                     h_stack()
@@ -2330,8 +2158,8 @@ impl CollabPanel {
     ) -> impl IntoElement {
         let github_login = SharedString::from(user.github_login.clone());
         let user_id = user.id;
-        let is_contact_request_pending = self.user_store.read(cx).is_contact_request_pending(&user);
-        let color = if is_contact_request_pending {
+        let is_response_pending = self.user_store.read(cx).is_contact_request_pending(&user);
+        let color = if is_response_pending {
             Color::Muted
         } else {
             Color::Default
@@ -2363,6 +2191,7 @@ impl CollabPanel {
         };
 
         ListItem::new(github_login.clone())
+            .selected(is_selected)
             .child(
                 h_stack()
                     .w_full()
@@ -2373,6 +2202,54 @@ impl CollabPanel {
             .start_slot(Avatar::new(user.avatar_uri.clone()))
     }
 
+    fn render_channel_invite(
+        &self,
+        channel: &Arc<Channel>,
+        is_selected: bool,
+        cx: &mut ViewContext<Self>,
+    ) -> impl IntoElement {
+        let channel_id = channel.id;
+        let response_is_pending = self
+            .channel_store
+            .read(cx)
+            .has_pending_channel_invite_response(&channel);
+        let color = if response_is_pending {
+            Color::Muted
+        } else {
+            Color::Default
+        };
+
+        let controls = [
+            IconButton::new("remove_contact", Icon::Close)
+                .on_click(cx.listener(move |this, _, cx| {
+                    this.respond_to_channel_invite(channel_id, false, cx);
+                }))
+                .icon_color(color)
+                .tooltip(|cx| Tooltip::text("Decline invite", cx)),
+            IconButton::new("remove_contact", Icon::Check)
+                .on_click(cx.listener(move |this, _, cx| {
+                    this.respond_to_contact_request(channel_id, true, cx);
+                }))
+                .icon_color(color)
+                .tooltip(|cx| Tooltip::text("Accept invite", cx)),
+        ];
+
+        ListItem::new(("channel-invite", channel.id as usize))
+            .selected(is_selected)
+            .child(
+                h_stack()
+                    .w_full()
+                    .justify_between()
+                    .child(Label::new(channel.name.clone()))
+                    .child(h_stack().children(controls)),
+            )
+            .start_slot(
+                IconElement::new(Icon::Hash)
+                    .size(IconSize::Small)
+                    .color(Color::Muted),
+            )
+    }
+
     fn render_contact_placeholder(
         &self,
         is_selected: bool,
@@ -2411,7 +2288,6 @@ impl CollabPanel {
             .channel_for_id(channel_id)
             .map(|channel| channel.visibility)
             == Some(proto::ChannelVisibility::Public);
-        let other_selected = self.selected_channel().map(|channel| channel.id) == Some(channel.id);
         let disclosed =
             has_children.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok());
 
@@ -2423,14 +2299,10 @@ impl CollabPanel {
 
         let face_pile = if !participants.is_empty() {
             let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
-            let user = &participants[0];
-
             let result = FacePile {
                 faces: participants
                     .iter()
-                    .filter_map(|user| {
-                        Some(Avatar::new(user.avatar_uri.clone()).into_any_element())
-                    })
+                    .map(|user| Avatar::new(user.avatar_uri.clone()).into_any_element())
                     .take(FACEPILE_LIMIT)
                     .chain(if extra_count > 0 {
                         // todo!() @nate - this label looks wrong.
@@ -2454,7 +2326,7 @@ impl CollabPanel {
             .flex()
             .w_full()
             .on_drag(channel.clone(), move |channel, cx| {
-                cx.build_view(|cx| DraggedChannelView {
+                cx.build_view(|_| DraggedChannelView {
                     channel: channel.clone(),
                     width,
                 })
@@ -2480,12 +2352,10 @@ impl CollabPanel {
                         }),
                     )
                     .on_click(cx.listener(move |this, _, cx| {
-                        if this.drag_target_channel == ChannelDragTarget::None {
-                            if is_active {
-                                this.open_channel_notes(channel_id, cx)
-                            } else {
-                                this.join_channel(channel_id, cx)
-                            }
+                        if is_active {
+                            this.open_channel_notes(channel_id, cx)
+                        } else {
+                            this.join_channel(channel_id, cx)
                         }
                     }))
                     .on_secondary_mouse_down(cx.listener(
@@ -2537,316 +2407,9 @@ impl CollabPanel {
                     ),
             )
             .tooltip(|cx| Tooltip::text("Join channel", cx))
+    }
 
-        // let channel_id = channel.id;
-        // let collab_theme = &theme.collab_panel;
-        // let is_public = self
-        //     .channel_store
-        //     .read(cx)
-        //     .channel_for_id(channel_id)
-        //     .map(|channel| channel.visibility)
-        //     == Some(proto::ChannelVisibility::Public);
-        // let other_selected = self.selected_channel().map(|channel| channel.id) == Some(channel.id);
-        // let disclosed =
-        //     has_children.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok());
-
-        // enum ChannelCall {}
-        // enum ChannelNote {}
-        // enum NotesTooltip {}
-        // enum ChatTooltip {}
-        // enum ChannelTooltip {}
-
-        // let mut is_dragged_over = false;
-        // if cx
-        //     .global::<DragAndDrop<Workspace>>()
-        //     .currently_dragged::<Channel>(cx.window())
-        //     .is_some()
-        //     && self.drag_target_channel == ChannelDragTarget::Channel(channel_id)
-        // {
-        //     is_dragged_over = true;
-        // }
-
-        // let has_messages_notification = channel.unseen_message_id.is_some();
-
-        // MouseEventHandler::new::<Channel, _>(ix, cx, |state, cx| {
-        //     let row_hovered = state.hovered();
-
-        //     let mut select_state = |interactive: &Interactive<ContainerStyle>| {
-        //         if state.clicked() == Some(MouseButton::Left) && interactive.clicked.is_some() {
-        //             interactive.clicked.as_ref().unwrap().clone()
-        //         } else if state.hovered() || other_selected {
-        //             interactive
-        //                 .hovered
-        //                 .as_ref()
-        //                 .unwrap_or(&interactive.default)
-        //                 .clone()
-        //         } else {
-        //             interactive.default.clone()
-        //         }
-        //     };
-
-        //     Flex::<Self>::row()
-        //         .with_child(
-        //             Svg::new(if is_public {
-        //                 "icons/public.svg"
-        //             } else {
-        //                 "icons/hash.svg"
-        //             })
-        //             .with_color(collab_theme.channel_hash.color)
-        //             .constrained()
-        //             .with_width(collab_theme.channel_hash.width)
-        //             .aligned()
-        //             .left(),
-        //         )
-        //         .with_child({
-        //             let style = collab_theme.channel_name.inactive_state();
-        //             Flex::row()
-        //                 .with_child(
-        //                     Label::new(channel.name.clone(), style.text.clone())
-        //                         .contained()
-        //                         .with_style(style.container)
-        //                         .aligned()
-        //                         .left()
-        //                         .with_tooltip::<ChannelTooltip>(
-        //                             ix,
-        //                             "Join channel",
-        //                             None,
-        //                             theme.tooltip.clone(),
-        //                             cx,
-        //                         ),
-        //                 )
-        //                 .with_children({
-        //                     let participants =
-        //                         self.channel_store.read(cx).channel_participants(channel_id);
-
-        //                     if !participants.is_empty() {
-        //                         let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
-
-        //                         let result = FacePile::new(collab_theme.face_overlap)
-        //                             .with_children(
-        //                                 participants
-        //                                     .iter()
-        //                                     .filter_map(|user| {
-        //                                         Some(
-        //                                             Image::from_data(user.avatar.clone()?)
-        //                                                 .with_style(collab_theme.channel_avatar),
-        //                                         )
-        //                                     })
-        //                                     .take(FACEPILE_LIMIT),
-        //                             )
-        //                             .with_children((extra_count > 0).then(|| {
-        //                                 Label::new(
-        //                                     format!("+{}", extra_count),
-        //                                     collab_theme.extra_participant_label.text.clone(),
-        //                                 )
-        //                                 .contained()
-        //                                 .with_style(collab_theme.extra_participant_label.container)
-        //                             }));
-
-        //                         Some(result)
-        //                     } else {
-        //                         None
-        //                     }
-        //                 })
-        //                 .with_spacing(8.)
-        //                 .align_children_center()
-        //                 .flex(1., true)
-        //         })
-        //         .with_child(
-        //             MouseEventHandler::new::<ChannelNote, _>(ix, cx, move |mouse_state, _| {
-        //                 let container_style = collab_theme
-        //                     .disclosure
-        //                     .button
-        //                     .style_for(mouse_state)
-        //                     .container;
-
-        //                 if channel.unseen_message_id.is_some() {
-        //                     Svg::new("icons/conversations.svg")
-        //                         .with_color(collab_theme.channel_note_active_color)
-        //                         .constrained()
-        //                         .with_width(collab_theme.channel_hash.width)
-        //                         .contained()
-        //                         .with_style(container_style)
-        //                         .with_uniform_padding(4.)
-        //                         .into_any()
-        //                 } else if row_hovered {
-        //                     Svg::new("icons/conversations.svg")
-        //                         .with_color(collab_theme.channel_hash.color)
-        //                         .constrained()
-        //                         .with_width(collab_theme.channel_hash.width)
-        //                         .contained()
-        //                         .with_style(container_style)
-        //                         .with_uniform_padding(4.)
-        //                         .into_any()
-        //                 } else {
-        //                     Empty::new().into_any()
-        //                 }
-        //             })
-        //             .on_click(MouseButton::Left, move |_, this, cx| {
-        //                 this.join_channel_chat(&JoinChannelChat { channel_id }, cx);
-        //             })
-        //             .with_tooltip::<ChatTooltip>(
-        //                 ix,
-        //                 "Open channel chat",
-        //                 None,
-        //                 theme.tooltip.clone(),
-        //                 cx,
-        //             )
-        //             .contained()
-        //             .with_margin_right(4.),
-        //         )
-        //         .with_child(
-        //             MouseEventHandler::new::<ChannelCall, _>(ix, cx, move |mouse_state, cx| {
-        //                 let container_style = collab_theme
-        //                     .disclosure
-        //                     .button
-        //                     .style_for(mouse_state)
-        //                     .container;
-        //                 if row_hovered || channel.unseen_note_version.is_some() {
-        //                     Svg::new("icons/file.svg")
-        //                         .with_color(if channel.unseen_note_version.is_some() {
-        //                             collab_theme.channel_note_active_color
-        //                         } else {
-        //                             collab_theme.channel_hash.color
-        //                         })
-        //                         .constrained()
-        //                         .with_width(collab_theme.channel_hash.width)
-        //                         .contained()
-        //                         .with_style(container_style)
-        //                         .with_uniform_padding(4.)
-        //                         .with_margin_right(collab_theme.channel_hash.container.margin.left)
-        //                         .with_tooltip::<NotesTooltip>(
-        //                             ix as usize,
-        //                             "Open channel notes",
-        //                             None,
-        //                             theme.tooltip.clone(),
-        //                             cx,
-        //                         )
-        //                         .into_any()
-        //                 } else if has_messages_notification {
-        //                     Empty::new()
-        //                         .constrained()
-        //                         .with_width(collab_theme.channel_hash.width)
-        //                         .contained()
-        //                         .with_uniform_padding(4.)
-        //                         .with_margin_right(collab_theme.channel_hash.container.margin.left)
-        //                         .into_any()
-        //                 } else {
-        //                     Empty::new().into_any()
-        //                 }
-        //             })
-        //             .on_click(MouseButton::Left, move |_, this, cx| {
-        //                 this.open_channel_notes(&OpenChannelNotes { channel_id }, cx);
-        //             }),
-        //         )
-        //         .align_children_center()
-        //         .styleable_component()
-        //         .disclosable(
-        //             disclosed,
-        //             Box::new(ToggleCollapse {
-        //                 location: channel.id.clone(),
-        //             }),
-        //         )
-        //         .with_id(ix)
-        //         .with_style(collab_theme.disclosure.clone())
-        //         .element()
-        //         .constrained()
-        //         .with_height(collab_theme.row_height)
-        //         .contained()
-        //         .with_style(select_state(
-        //             collab_theme
-        //                 .channel_row
-        //                 .in_state(is_selected || is_active || is_dragged_over),
-        //         ))
-        //         .with_padding_left(
-        //             collab_theme.channel_row.default_style().padding.left
-        //                 + collab_theme.channel_indent * depth as f32,
-        //         )
-        // })
-        // .on_click(MouseButton::Left, move |_, this, cx| {
-        //     if this.
-        // drag_target_channel == ChannelDragTarget::None {
-        //         if is_active {
-        //             this.open_channel_notes(&OpenChannelNotes { channel_id }, cx)
-        //         } else {
-        //             this.join_channel(channel_id, cx)
-        //         }
-        //     }
-        // })
-        // .on_click(MouseButton::Right, {
-        //     let channel = channel.clone();
-        //     move |e, this, cx| {
-        //         this.deploy_channel_context_menu(Some(e.position), &channel, ix, cx);
-        //     }
-        // })
-        // .on_up(MouseButton::Left, move |_, this, cx| {
-        //     if let Some((_, dragged_channel)) = cx
-        //         .global::<DragAndDrop<Workspace>>()
-        //         .currently_dragged::<Channel>(cx.window())
-        //     {
-        //         this.channel_store
-        //             .update(cx, |channel_store, cx| {
-        //                 channel_store.move_channel(dragged_channel.id, Some(channel_id), cx)
-        //             })
-        //             .detach_and_log_err(cx)
-        //     }
-        // })
-        // .on_move({
-        //     let channel = channel.clone();
-        //     move |_, this, cx| {
-        //         if let Some((_, dragged_channel)) = cx
-        //             .global::<DragAndDrop<Workspace>>()
-        //             .currently_dragged::<Channel>(cx.window())
-        //         {
-        //             if channel.id != dragged_channel.id {
-        //                 this.drag_target_channel = ChannelDragTarget::Channel(channel.id);
-        //             }
-        //             cx.notify()
-        //         }
-        //     }
-        // })
-        // .as_draggable::<_, Channel>(
-        //     channel.clone(),
-        //     move |_, channel, cx: &mut ViewContext<Workspace>| {
-        //         let theme = &theme::current(cx).collab_panel;
-
-        //         Flex::<Workspace>::row()
-        //             .with_child(
-        //                 Svg::new("icons/hash.svg")
-        //                     .with_color(theme.channel_hash.color)
-        //                     .constrained()
-        //                     .with_width(theme.channel_hash.width)
-        //                     .aligned()
-        //                     .left(),
-        //             )
-        //             .with_child(
-        //                 Label::new(channel.name.clone(), theme.channel_name.text.clone())
-        //                     .contained()
-        //                     .with_style(theme.channel_name.container)
-        //                     .aligned()
-        //                     .left(),
-        //             )
-        //             .align_children_center()
-        //             .contained()
-        //             .with_background_color(
-        //                 theme
-        //                     .container
-        //                     .background_color
-        //                     .unwrap_or(gpui::color::Color::transparent_black()),
-        //             )
-        //             .contained()
-        //             .with_padding_left(
-        //                 theme.channel_row.default_style().padding.left
-        //                     + theme.channel_indent * depth as f32,
-        //             )
-        //             .into_any()
-        //     },
-        // )
-        // .with_cursor_style(CursorStyle::PointingHand)
-        // .into_any()
-    }
-
-    fn render_channel_editor(&self, depth: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
+    fn render_channel_editor(&self, depth: usize, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let item = ListItem::new("channel-editor")
             .inset(false)
             .indent_level(depth)
@@ -2916,20 +2479,12 @@ impl Render for CollabPanel {
             .on_action(cx.listener(CollabPanel::select_prev))
             .on_action(cx.listener(CollabPanel::confirm))
             .on_action(cx.listener(CollabPanel::insert_space))
-            //     .on_action(cx.listener(CollabPanel::remove))
             .on_action(cx.listener(CollabPanel::remove_selected_channel))
             .on_action(cx.listener(CollabPanel::show_inline_context_menu))
-            //     .on_action(cx.listener(CollabPanel::new_subchannel))
-            //     .on_action(cx.listener(CollabPanel::invite_members))
-            //     .on_action(cx.listener(CollabPanel::manage_members))
             .on_action(cx.listener(CollabPanel::rename_selected_channel))
-            //     .on_action(cx.listener(CollabPanel::rename_channel))
-            //     .on_action(cx.listener(CollabPanel::toggle_channel_collapsed_action))
             .on_action(cx.listener(CollabPanel::collapse_selected_channel))
             .on_action(cx.listener(CollabPanel::expand_selected_channel))
-            //     .on_action(cx.listener(CollabPanel::open_channel_notes))
-            //     .on_action(cx.listener(CollabPanel::join_channel_chat))
-            //     .on_action(cx.listener(CollabPanel::copy_channel_link))
+            .on_action(cx.listener(CollabPanel::start_move_selected_channel))
             .track_focus(&self.focus_handle)
             .size_full()
             .child(if self.user_store.read(cx).current_user().is_none() {
@@ -2946,105 +2501,6 @@ impl Render for CollabPanel {
     }
 }
 
-// impl View for CollabPanel {
-//     fn ui_name() -> &'static str {
-//         "CollabPanel"
-//     }
-
-//     fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
-//         if !self.has_focus {
-//             self.has_focus = true;
-//             if !self.context_menu.is_focused(cx) {
-//                 if let Some(editing_state) = &self.channel_editing_state {
-//                     if editing_state.pending_name().is_none() {
-//                         cx.focus(&self.channel_name_editor);
-//                     } else {
-//                         cx.focus(&self.filter_editor);
-//                     }
-//                 } else {
-//                     cx.focus(&self.filter_editor);
-//                 }
-//             }
-//             cx.emit(Event::Focus);
-//         }
-//     }
-
-//     fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
-//         self.has_focus = false;
-//     }
-
-//     fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
-//         let theme = &theme::current(cx).collab_panel;
-
-//         if self.user_store.read(cx).current_user().is_none() {
-//             enum LogInButton {}
-
-//             return Flex::column()
-//                 .with_child(
-//                     MouseEventHandler::new::<LogInButton, _>(0, cx, |state, _| {
-//                         let button = theme.log_in_button.style_for(state);
-//                         Label::new("Sign in to collaborate", button.text.clone())
-//                             .aligned()
-//                             .left()
-//                             .contained()
-//                             .with_style(button.container)
-//                     })
-//                     .on_click(MouseButton::Left, |_, this, cx| {
-//                         let client = this.client.clone();
-//                         cx.spawn(|_, cx| async move {
-//                             client.authenticate_and_connect(true, &cx).await.log_err();
-//                         })
-//                         .detach();
-//                     })
-//                     .with_cursor_style(CursorStyle::PointingHand),
-//                 )
-//                 .contained()
-//                 .with_style(theme.container)
-//                 .into_any();
-//         }
-
-//         enum PanelFocus {}
-//         MouseEventHandler::new::<PanelFocus, _>(0, cx, |_, cx| {
-//             Stack::new()
-//                 .with_child(
-//                     Flex::column()
-//                         .with_child(
-//                             Flex::row().with_child(
-//                                 ChildView::new(&self.filter_editor, cx)
-//                                     .contained()
-//                                     .with_style(theme.user_query_editor.container)
-//                                     .flex(1.0, true),
-//                             ),
-//                         )
-//                         .with_child(List::new(self.list_state.clone()).flex(1., true).into_any())
-//                         .contained()
-//                         .with_style(theme.container)
-//                         .into_any(),
-//                 )
-//                 .with_children(
-//                     (!self.context_menu_on_selected)
-//                         .then(|| ChildView::new(&self.context_menu, cx)),
-//                 )
-//                 .into_any()
-//         })
-//         .on_click(MouseButton::Left, |_, _, cx| cx.focus_self())
-//         .into_any_named("collab panel")
-//     }
-
-//     fn update_keymap_context(
-//         &self,
-//         keymap: &mut gpui::keymap_matcher::KeymapContext,
-//         _: &AppContext,
-//     ) {
-//         Self::reset_to_default_keymap_context(keymap);
-//         if self.channel_editing_state.is_some() {
-//             keymap.add_identifier("editing");
-//         } else {
-//             keymap.add_identifier("not_editing");
-//         }
-//     }
-// }
-
 impl EventEmitter<PanelEvent> for CollabPanel {}
 
 impl Panel for CollabPanel {