WIP: add custom channel modal

Mikayla Maki created

Change summary

crates/client/src/channel_store.rs          |  4 +-
crates/collab_ui/src/panel.rs               | 23 ++++++++++++++
crates/collab_ui/src/panel/channel_modal.rs | 36 +++++++++++++++++++---
crates/theme/src/theme.rs                   |  6 +++
styles/src/style_tree/channel_modal.ts      |  9 +++++
styles/src/style_tree/collab_panel.ts       |  2 +
6 files changed, 72 insertions(+), 8 deletions(-)

Detailed changes

crates/client/src/channel_store.rs 🔗

@@ -6,8 +6,8 @@ use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
 use rpc::{proto, TypedEnvelope};
 use std::sync::Arc;
 
-type ChannelId = u64;
-type UserId = u64;
+pub type ChannelId = u64;
+pub type UserId = u64;
 
 pub struct ChannelStore {
     channels: Vec<Arc<Channel>>,

crates/collab_ui/src/panel.rs 🔗

@@ -42,6 +42,8 @@ use workspace::{
 
 use crate::face_pile::FacePile;
 
+use self::channel_modal::ChannelModal;
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 struct RemoveChannel {
     channel_id: u64,
@@ -52,9 +54,14 @@ struct NewChannel {
     channel_id: u64,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+struct AddMember {
+    channel_id: u64,
+}
+
 actions!(collab_panel, [ToggleFocus]);
 
-impl_actions!(collab_panel, [RemoveChannel, NewChannel]);
+impl_actions!(collab_panel, [RemoveChannel, NewChannel, AddMember]);
 
 const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel";
 
@@ -69,6 +76,7 @@ pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
     cx.add_action(CollabPanel::confirm);
     cx.add_action(CollabPanel::remove_channel);
     cx.add_action(CollabPanel::new_subchannel);
+    cx.add_action(CollabPanel::add_member);
 }
 
 #[derive(Debug, Default)]
@@ -1506,6 +1514,7 @@ impl CollabPanel {
                 vec![
                     ContextMenuItem::action("New Channel", NewChannel { channel_id }),
                     ContextMenuItem::action("Remove Channel", RemoveChannel { channel_id }),
+                    ContextMenuItem::action("Add member", AddMember { channel_id }),
                 ],
                 cx,
             );
@@ -1668,6 +1677,18 @@ impl CollabPanel {
         cx.notify();
     }
 
+    fn add_member(&mut self, action: &AddMember, cx: &mut ViewContext<Self>) {
+        if let Some(workspace) = self.workspace.upgrade(cx) {
+            workspace.update(cx, |workspace, cx| {
+                workspace.toggle_modal(cx, |_, cx| {
+                    cx.add_view(|cx| {
+                        ChannelModal::new(action.channel_id, self.channel_store.clone(), cx)
+                    })
+                })
+            });
+        }
+    }
+
     fn remove_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
         let channel_id = action.channel_id;
         let channel_store = self.channel_store.clone();

crates/collab_ui/src/panel/channel_modal.rs 🔗

@@ -1,5 +1,8 @@
+use client::{ChannelId, ChannelStore};
 use editor::Editor;
-use gpui::{elements::*, AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle};
+use gpui::{
+    elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, View, ViewContext, ViewHandle,
+};
 use menu::Cancel;
 use workspace::{item::ItemHandle, Modal};
 
@@ -10,6 +13,10 @@ pub fn init(cx: &mut AppContext) {
 pub struct ChannelModal {
     has_focus: bool,
     filter_editor: ViewHandle<Editor>,
+    selection: usize,
+    list_state: ListState<Self>,
+    channel_store: ModelHandle<ChannelStore>,
+    channel_id: ChannelId,
 }
 
 pub enum Event {
@@ -21,16 +28,28 @@ impl Entity for ChannelModal {
 }
 
 impl ChannelModal {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+    pub fn new(
+        channel_id: ChannelId,
+        channel_store: ModelHandle<ChannelStore>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
         let input_editor = cx.add_view(|cx| {
             let mut editor = Editor::single_line(None, cx);
-            editor.set_placeholder_text("Create or add a channel", cx);
+            editor.set_placeholder_text("Add a member", cx);
             editor
         });
 
+        let list_state = ListState::<Self>::new(0, Orientation::Top, 1000., move |this, ix, cx| {
+            Empty::new().into_any()
+        });
+
         ChannelModal {
             has_focus: false,
             filter_editor: input_editor,
+            selection: 0,
+            list_state,
+            channel_id,
+            channel_store,
         }
     }
 
@@ -49,14 +68,21 @@ impl View for ChannelModal {
     }
 
     fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
-        let style = theme::current(cx).editor.hint_diagnostic.message.clone();
+        let theme = theme::current(cx).clone();
+        let style = &theme.collab_panel.modal;
         let modal_container = theme::current(cx).picker.container.clone();
 
         enum ChannelModal {}
         MouseEventHandler::<ChannelModal, _>::new(0, cx, |_, cx| {
             Flex::column()
                 .with_child(ChildView::new(self.filter_editor.as_any(), cx))
-                .with_child(Label::new("ADD OR BROWSE CHANNELS HERE", style))
+                .with_child(
+                    List::new(self.list_state.clone())
+                        .constrained()
+                        .with_width(style.width)
+                        .flex(1., true)
+                        .into_any(),
+                )
                 .contained()
                 .with_style(modal_container)
                 .constrained()

crates/theme/src/theme.rs 🔗

@@ -220,6 +220,7 @@ pub struct CopilotAuthAuthorized {
 pub struct CollabPanel {
     #[serde(flatten)]
     pub container: ContainerStyle,
+    pub modal: ChannelModal,
     pub user_query_editor: FieldEditor,
     pub user_query_editor_height: f32,
     pub leave_call_button: IconButton,
@@ -244,6 +245,11 @@ pub struct CollabPanel {
     pub face_overlap: f32,
 }
 
+#[derive(Deserialize, Default, JsonSchema)]
+pub struct ChannelModal {
+    pub width: f32,
+}
+
 #[derive(Deserialize, Default, JsonSchema)]
 pub struct ProjectRow {
     #[serde(flatten)]

styles/src/style_tree/channel_modal.ts 🔗

@@ -0,0 +1,9 @@
+import { useTheme } from "../theme"
+
+export default function contacts_panel(): any {
+    const theme = useTheme()
+
+    return {
+        width: 100,
+    }
+}

styles/src/style_tree/collab_panel.ts 🔗

@@ -7,6 +7,7 @@ import {
 } from "./components"
 import { interactive, toggleable } from "../element"
 import { useTheme } from "../theme"
+import channel_modal from "./channel_modal"
 
 
 export default function contacts_panel(): any {
@@ -51,6 +52,7 @@ export default function contacts_panel(): any {
     }
 
     return {
+        modal: channel_modal(),
         background: background(layer),
         padding: {
             top: 12,