@@ -1,129 +1,45 @@
-#![allow(unused)]
mod channel_modal;
mod contact_finder;
-// use crate::{
-// channel_view::{self, ChannelView},
-// chat_panel::ChatPanel,
-// face_pile::FacePile,
-// panel_settings, CollaborationPanelSettings,
-// };
-// use anyhow::Result;
-// use call::ActiveCall;
-// use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
-// use channel_modal::ChannelModal;
-// use client::{
-// proto::{self, PeerId},
-// Client, Contact, User, UserStore,
-// };
+use self::channel_modal::ChannelModal;
+use crate::{
+ channel_view::ChannelView, chat_panel::ChatPanel, face_pile::FacePile,
+ CollaborationPanelSettings,
+};
+use call::ActiveCall;
+use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
+use client::{Client, Contact, User, UserStore};
use contact_finder::ContactFinder;
+use db::kvp::KEY_VALUE_STORE;
+use editor::Editor;
+use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
+use fuzzy::{match_strings, StringMatchCandidate};
+use gpui::{
+ actions, canvas, div, fill, list, overlay, point, prelude::*, px, serde_json, AnyElement,
+ AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
+ FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement, ListOffset, ListState,
+ Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce,
+ SharedString, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
+};
use menu::{Cancel, Confirm, SelectNext, SelectPrev};
+use project::{Fs, Project};
use rpc::proto::{self, PeerId};
+use serde_derive::{Deserialize, Serialize};
+use settings::{Settings, SettingsStore};
use smallvec::SmallVec;
+use std::{mem, sync::Arc};
use theme::{ActiveTheme, ThemeSettings};
-// use context_menu::{ContextMenu, ContextMenuItem};
-// use db::kvp::KEY_VALUE_STORE;
-// use drag_and_drop::{DragAndDrop, Draggable};
-// use editor::{Cancel, Editor};
-// use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
-// use futures::StreamExt;
-// use fuzzy::{match_strings, StringMatchCandidate};
-// use gpui::{
-// actions,
-// elements::{
-// Canvas, ChildView, Component, ContainerStyle, Empty, Flex, Image, Label, List, ListOffset,
-// ListState, MouseEventHandler, Orientation, OverlayPositionMode, Padding, ParentElement,
-// SafeStylable, Stack, Svg,
-// },
-// fonts::TextStyle,
-// geometry::{
-// rect::RectF,
-// vector::{vec2f, Vector2F},
-// },
-// impl_actions,
-// platform::{CursorStyle, MouseButton, PromptLevel},
-// serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, FontCache,
-// ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
-// };
-// use menu::{Confirm, SelectNext, SelectPrev};
-// use project::{Fs, Project};
-// use serde_derive::{Deserialize, Serialize};
-// use settings::SettingsStore;
-// use std::{borrow::Cow, hash::Hash, mem, sync::Arc};
-// use theme::{components::ComponentExt, IconButton, Interactive};
-// use util::{maybe, ResultExt, TryFutureExt};
-// use workspace::{
-// dock::{DockPosition, Panel},
-// item::ItemHandle,
-// FollowNextCollaborator, Workspace,
-// };
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct ToggleCollapse {
-// location: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct NewChannel {
-// location: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct RenameChannel {
-// channel_id: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct ToggleSelectedIx {
-// ix: usize,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct RemoveChannel {
-// channel_id: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct InviteMembers {
-// channel_id: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct ManageMembers {
-// channel_id: ChannelId,
-// }
-
-#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
-pub struct OpenChannelNotes {
- pub channel_id: ChannelId,
-}
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// pub struct JoinChannelCall {
-// pub channel_id: u64,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// pub struct JoinChannelChat {
-// pub channel_id: u64,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// pub struct CopyChannelLink {
-// pub channel_id: u64,
-// }
-
-// #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct StartMoveChannelFor {
-// channel_id: ChannelId,
-// }
-
-// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-// struct MoveChannel {
-// to: ChannelId,
-// }
-
-impl_actions!(collab_panel, [OpenChannelNotes]);
+use ui::prelude::*;
+use ui::{
+ h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize,
+ Label, ListHeader, ListItem, Tooltip,
+};
+use util::{maybe, ResultExt, TryFutureExt};
+use workspace::{
+ dock::{DockPosition, Panel, PanelEvent},
+ notifications::NotifyResultExt,
+ Workspace,
+};
actions!(
collab_panel,
@@ -139,25 +55,6 @@ actions!(
]
);
-// impl_actions!(
-// collab_panel,
-// [
-// RemoveChannel,
-// NewChannel,
-// InviteMembers,
-// ManageMembers,
-// RenameChannel,
-// ToggleCollapse,
-// OpenChannelNotes,
-// JoinChannelCall,
-// JoinChannelChat,
-// CopyChannelLink,
-// StartMoveChannelFor,
-// MoveChannel,
-// ToggleSelectedIx
-// ]
-// );
-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct ChannelMoveClipboard {
channel_id: ChannelId,
@@ -165,44 +62,6 @@ struct ChannelMoveClipboard {
const COLLABORATION_PANEL_KEY: &'static str = "CollaborationPanel";
-use std::{mem, sync::Arc};
-
-use call::ActiveCall;
-use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
-use client::{Client, Contact, User, UserStore};
-use db::kvp::KEY_VALUE_STORE;
-use editor::Editor;
-use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
-use fuzzy::{match_strings, StringMatchCandidate};
-use gpui::{
- actions, canvas, div, fill, impl_actions, list, overlay, point, prelude::*, px, serde_json,
- AnyElement, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div,
- EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement,
- ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
- Render, RenderOnce, SharedString, Styled, Subscription, Task, View, ViewContext, VisualContext,
- WeakView,
-};
-use project::{Fs, Project};
-use serde_derive::{Deserialize, Serialize};
-use settings::{Settings, SettingsStore};
-use ui::prelude::*;
-use ui::{
- h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize,
- Label, ListHeader, ListItem, Tooltip,
-};
-use util::{maybe, ResultExt, TryFutureExt};
-use workspace::{
- dock::{DockPosition, Panel, PanelEvent},
- notifications::NotifyResultExt,
- Workspace,
-};
-
-use crate::channel_view::ChannelView;
-use crate::chat_panel::ChatPanel;
-use crate::{face_pile::FacePile, CollaborationPanelSettings};
-
-use self::channel_modal::ChannelModal;
-
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
@@ -210,69 +69,6 @@ pub fn init(cx: &mut AppContext) {
});
})
.detach();
- // contact_finder::init(cx);
- // channel_modal::init(cx);
- // channel_view::init(cx);
-
- // cx.add_action(CollabPanel::cancel);
- // cx.add_action(CollabPanel::select_next);
- // cx.add_action(CollabPanel::select_prev);
- // cx.add_action(CollabPanel::confirm);
- // cx.add_action(CollabPanel::insert_space);
- // cx.add_action(CollabPanel::remove);
- // cx.add_action(CollabPanel::remove_selected_channel);
- // cx.add_action(CollabPanel::show_inline_context_menu);
- // cx.add_action(CollabPanel::new_subchannel);
- // cx.add_action(CollabPanel::invite_members);
- // cx.add_action(CollabPanel::manage_members);
- // cx.add_action(CollabPanel::rename_selected_channel);
- // cx.add_action(CollabPanel::rename_channel);
- // cx.add_action(CollabPanel::toggle_channel_collapsed_action);
- // cx.add_action(CollabPanel::collapse_selected_channel);
- // cx.add_action(CollabPanel::expand_selected_channel);
- // cx.add_action(CollabPanel::open_channel_notes);
- // cx.add_action(CollabPanel::join_channel_chat);
- // cx.add_action(CollabPanel::copy_channel_link);
-
- // cx.add_action(
- // |panel: &mut CollabPanel, action: &ToggleSelectedIx, cx: &mut ViewContext<CollabPanel>| {
- // if panel.selection.take() != Some(action.ix) {
- // panel.selection = Some(action.ix)
- // }
-
- // cx.notify();
- // },
- // );
-
- // cx.add_action(
- // |panel: &mut CollabPanel, _: &MoveSelected, cx: &mut ViewContext<CollabPanel>| {
- // let Some(clipboard) = panel.channel_clipboard.take() else {
- // return;
- // };
- // let Some(selected_channel) = panel.selected_channel() else {
- // return;
- // };
-
- // panel
- // .channel_store
- // .update(cx, |channel_store, cx| {
- // channel_store.move_channel(clipboard.channel_id, Some(selected_channel.id), cx)
- // })
- // .detach_and_log_err(cx)
- // },
- // );
-
- // cx.add_action(
- // |panel: &mut CollabPanel, action: &MoveChannel, cx: &mut ViewContext<CollabPanel>| {
- // if let Some(clipboard) = panel.channel_clipboard.take() {
- // panel.channel_store.update(cx, |channel_store, cx| {
- // channel_store
- // .move_channel(clipboard.channel_id, Some(action.to), cx)
- // .detach_and_log_err(cx)
- // })
- // }
- // },
- // );
}
#[derive(Debug)]
@@ -317,16 +113,7 @@ 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)]
@@ -335,13 +122,6 @@ struct SerializedCollabPanel {
collapsed_channels: Option<Vec<u64>>,
}
-// #[derive(Debug)]
-// pub enum Event {
-// DockPositionChanged,
-// Focus,
-// Dismissed,
-// }
-
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
enum Section {
ActiveCall,
@@ -373,7 +153,7 @@ enum ListEntry {
},
IncomingRequest(Arc<User>),
OutgoingRequest(Arc<User>),
- // ChannelInvite(Arc<Channel>),
+ ChannelInvite(Arc<Channel>),
Channel {
channel: Arc<Channel>,
depth: usize,
@@ -472,8 +252,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 +307,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 +583,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 +824,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 +834,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 +847,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 +858,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 +877,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 +899,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))
})
@@ -1219,40 +920,6 @@ impl CollabPanel {
}
}
- // fn render_contact_placeholder(
- // &self,
- // theme: &theme::CollabPanel,
- // is_selected: bool,
- // cx: &mut ViewContext<Self>,
- // ) -> AnyElement<Self> {
- // enum AddContacts {}
- // MouseEventHandler::new::<AddContacts, _>(0, cx, |state, _| {
- // let style = theme.list_empty_state.style_for(is_selected, state);
- // Flex::row()
- // .with_child(
- // Svg::new("icons/plus.svg")
- // .with_color(theme.list_empty_icon.color)
- // .constrained()
- // .with_width(theme.list_empty_icon.width)
- // .aligned()
- // .left(),
- // )
- // .with_child(
- // Label::new("Add a contact", style.text.clone())
- // .contained()
- // .with_style(theme.list_empty_label_container),
- // )
- // .align_children_center()
- // .contained()
- // .with_style(style.container)
- // .into_any()
- // })
- // .on_click(MouseButton::Left, |_, this, cx| {
- // this.toggle_contact_finder(cx);
- // })
- // .into_any()
- // }
-
fn render_channel_notes(
&self,
channel_id: ChannelId,
@@ -1291,86 +958,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 +1311,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 +1400,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 +1485,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 +1492,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
@@ -1928,17 +1507,13 @@ impl CollabPanel {
}
}
- // // Should move to the filter editor if clicking on it
- // // Should move selection to the channel editor if activating it
-
fn remove_contact(&mut self, user_id: u64, github_login: &str, cx: &mut ViewContext<Self>) {
let user_store = self.user_store.clone();
let prompt_message = format!(
"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 +1539,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 +1671,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 +1692,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 +1812,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 +1844,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 +1910,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
@@ -2339,13 +1919,13 @@ impl CollabPanel {
let controls = if is_incoming {
vec![
- IconButton::new("remove_contact", Icon::Close)
+ IconButton::new("decline-contact", Icon::Close)
.on_click(cx.listener(move |this, _, cx| {
this.respond_to_contact_request(user_id, false, cx);
}))
.icon_color(color)
.tooltip(|cx| Tooltip::text("Decline invite", cx)),
- IconButton::new("remove_contact", Icon::Check)
+ IconButton::new("accept-contact", Icon::Check)
.on_click(cx.listener(move |this, _, cx| {
this.respond_to_contact_request(user_id, true, cx);
}))
@@ -2363,6 +1943,7 @@ impl CollabPanel {
};
ListItem::new(github_login.clone())
+ .selected(is_selected)
.child(
h_stack()
.w_full()
@@ -2373,6 +1954,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("reject-invite", 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("accept-invite", Icon::Check)
+ .on_click(cx.listener(move |this, _, cx| {
+ this.respond_to_channel_invite(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 +2040,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 +2051,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 +2078,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 +2104,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(
@@ -5,13 +5,13 @@ use client::{
};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
- actions, div, AppContext, ClipboardItem, DismissEvent, Div, Entity, EventEmitter,
- FocusableView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext,
- WeakView,
+ actions, div, overlay, AppContext, ClipboardItem, DismissEvent, Div, EventEmitter,
+ FocusableView, Model, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
+ VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
-use ui::prelude::*;
+use ui::{prelude::*, Avatar, Checkbox, ContextMenu, ListItem};
use util::TryFutureExt;
use workspace::ModalView;
@@ -25,19 +25,10 @@ actions!(
]
);
-// pub fn init(cx: &mut AppContext) {
-// Picker::<ChannelModalDelegate>::init(cx);
-// cx.add_action(ChannelModal::toggle_mode);
-// cx.add_action(ChannelModal::toggle_member_admin);
-// cx.add_action(ChannelModal::remove_member);
-// cx.add_action(ChannelModal::dismiss);
-// }
-
pub struct ChannelModal {
picker: View<Picker<ChannelModalDelegate>>,
channel_store: Model<ChannelStore>,
channel_id: ChannelId,
- has_focus: bool,
}
impl ChannelModal {
@@ -62,25 +53,19 @@ impl ChannelModal {
channel_store: channel_store.clone(),
channel_id,
match_candidates: Vec::new(),
+ context_menu: None,
members,
mode,
- // context_menu: cx.add_view(|cx| {
- // let mut menu = ContextMenu::new(cx.view_id(), cx);
- // menu.set_position_mode(OverlayPositionMode::Local);
- // menu
- // }),
},
cx,
)
+ .modal(false)
});
- let has_focus = picker.focus_handle(cx).contains_focused(cx);
-
Self {
picker,
channel_store,
channel_id,
- has_focus,
}
}
@@ -126,15 +111,19 @@ impl ChannelModal {
.detach();
}
- fn toggle_member_admin(&mut self, _: &ToggleMemberAdmin, cx: &mut ViewContext<Self>) {
- self.picker.update(cx, |picker, cx| {
- picker.delegate.toggle_selected_member_admin(cx);
- })
- }
-
- fn remove_member(&mut self, _: &RemoveMember, cx: &mut ViewContext<Self>) {
- self.picker.update(cx, |picker, cx| {
- picker.delegate.remove_selected_member(cx);
+ fn set_channel_visiblity(&mut self, selection: &Selection, cx: &mut ViewContext<Self>) {
+ self.channel_store.update(cx, |channel_store, cx| {
+ channel_store
+ .set_channel_visibility(
+ self.channel_id,
+ match selection {
+ Selection::Unselected => ChannelVisibility::Members,
+ Selection::Selected => ChannelVisibility::Public,
+ Selection::Indeterminate => return,
+ },
+ cx,
+ )
+ .detach_and_log_err(cx)
});
}
@@ -156,167 +145,87 @@ impl Render for ChannelModal {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- v_stack().w(rems(34.)).child(self.picker.clone())
- // let theme = &theme::current(cx).collab_panel.tabbed_modal;
-
- // let mode = self.picker.read(cx).delegate().mode;
- // let Some(channel) = self.channel_store.read(cx).channel_for_id(self.channel_id) else {
- // return Empty::new().into_any();
- // };
-
- // enum InviteMembers {}
- // enum ManageMembers {}
-
- // fn render_mode_button<T: 'static>(
- // mode: Mode,
- // text: &'static str,
- // current_mode: Mode,
- // theme: &theme::TabbedModal,
- // cx: &mut ViewContext<ChannelModal>,
- // ) -> AnyElement<ChannelModal> {
- // let active = mode == current_mode;
- // MouseEventHandler::new::<T, _>(0, cx, move |state, _| {
- // let contained_text = theme.tab_button.style_for(active, state);
- // Label::new(text, contained_text.text.clone())
- // .contained()
- // .with_style(contained_text.container.clone())
- // })
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // if !active {
- // this.set_mode(mode, cx);
- // }
- // })
- // .with_cursor_style(CursorStyle::PointingHand)
- // .into_any()
- // }
-
- // fn render_visibility(
- // channel_id: ChannelId,
- // visibility: ChannelVisibility,
- // theme: &theme::TabbedModal,
- // cx: &mut ViewContext<ChannelModal>,
- // ) -> AnyElement<ChannelModal> {
- // enum TogglePublic {}
-
- // if visibility == ChannelVisibility::Members {
- // return Flex::row()
- // .with_child(
- // MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
- // let style = theme.visibility_toggle.style_for(state);
- // Label::new(format!("{}", "Public access: OFF"), style.text.clone())
- // .contained()
- // .with_style(style.container.clone())
- // })
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // this.channel_store
- // .update(cx, |channel_store, cx| {
- // channel_store.set_channel_visibility(
- // channel_id,
- // ChannelVisibility::Public,
- // cx,
- // )
- // })
- // .detach_and_log_err(cx);
- // })
- // .with_cursor_style(CursorStyle::PointingHand),
- // )
- // .into_any();
- // }
-
- // Flex::row()
- // .with_child(
- // MouseEventHandler::new::<TogglePublic, _>(0, cx, move |state, _| {
- // let style = theme.visibility_toggle.style_for(state);
- // Label::new(format!("{}", "Public access: ON"), style.text.clone())
- // .contained()
- // .with_style(style.container.clone())
- // })
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // this.channel_store
- // .update(cx, |channel_store, cx| {
- // channel_store.set_channel_visibility(
- // channel_id,
- // ChannelVisibility::Members,
- // cx,
- // )
- // })
- // .detach_and_log_err(cx);
- // })
- // .with_cursor_style(CursorStyle::PointingHand),
- // )
- // .with_spacing(14.0)
- // .with_child(
- // MouseEventHandler::new::<TogglePublic, _>(1, cx, move |state, _| {
- // let style = theme.channel_link.style_for(state);
- // Label::new(format!("{}", "copy link"), style.text.clone())
- // .contained()
- // .with_style(style.container.clone())
- // })
- // .on_click(MouseButton::Left, move |_, this, cx| {
- // if let Some(channel) =
- // this.channel_store.read(cx).channel_for_id(channel_id)
- // {
- // let item = ClipboardItem::new(channel.link());
- // cx.write_to_clipboard(item);
- // }
- // })
- // .with_cursor_style(CursorStyle::PointingHand),
- // )
- // .into_any()
- // }
-
- // Flex::column()
- // .with_child(
- // Flex::column()
- // .with_child(
- // Label::new(format!("#{}", channel.name), theme.title.text.clone())
- // .contained()
- // .with_style(theme.title.container.clone()),
- // )
- // .with_child(render_visibility(channel.id, channel.visibility, theme, cx))
- // .with_child(Flex::row().with_children([
- // render_mode_button::<InviteMembers>(
- // Mode::InviteMembers,
- // "Invite members",
- // mode,
- // theme,
- // cx,
- // ),
- // render_mode_button::<ManageMembers>(
- // Mode::ManageMembers,
- // "Manage members",
- // mode,
- // theme,
- // cx,
- // ),
- // ]))
- // .expanded()
- // .contained()
- // .with_style(theme.header),
- // )
- // .with_child(
- // ChildView::new(&self.picker, cx)
- // .contained()
- // .with_style(theme.body),
- // )
- // .constrained()
- // .with_max_height(theme.max_height)
- // .with_max_width(theme.max_width)
- // .contained()
- // .with_style(theme.modal)
- // .into_any()
+ let channel_store = self.channel_store.read(cx);
+ let Some(channel) = channel_store.channel_for_id(self.channel_id) else {
+ return div();
+ };
+ let channel_name = channel.name.clone();
+ let channel_id = channel.id;
+ let visibility = channel.visibility;
+ let mode = self.picker.read(cx).delegate.mode;
+
+ v_stack()
+ .key_context("ChannelModal")
+ .on_action(cx.listener(Self::toggle_mode))
+ .on_action(cx.listener(Self::dismiss))
+ .elevation_3(cx)
+ .w(rems(34.))
+ .child(
+ v_stack()
+ .px_2()
+ .py_1()
+ .rounded_t(px(8.))
+ .bg(cx.theme().colors().element_background)
+ .child(IconElement::new(Icon::Hash).size(IconSize::Medium))
+ .child(Label::new(channel_name))
+ .child(
+ h_stack()
+ .w_full()
+ .justify_between()
+ .child(
+ h_stack()
+ .gap_2()
+ .child(
+ Checkbox::new(
+ "is-public",
+ if visibility == ChannelVisibility::Public {
+ ui::Selection::Selected
+ } else {
+ ui::Selection::Unselected
+ },
+ )
+ .on_click(cx.listener(Self::set_channel_visiblity)),
+ )
+ .child(Label::new("Public")),
+ )
+ .children(if visibility == ChannelVisibility::Public {
+ Some(Button::new("copy-link", "Copy Link").on_click(cx.listener(
+ move |this, _, cx| {
+ if let Some(channel) =
+ this.channel_store.read(cx).channel_for_id(channel_id)
+ {
+ let item = ClipboardItem::new(channel.link());
+ cx.write_to_clipboard(item);
+ }
+ },
+ )))
+ } else {
+ None
+ }),
+ )
+ .child(
+ div()
+ .w_full()
+ .flex()
+ .flex_row()
+ .child(
+ Button::new("manage-members", "Manage Members")
+ .selected(mode == Mode::ManageMembers)
+ .on_click(cx.listener(|this, _, cx| {
+ this.set_mode(Mode::ManageMembers, cx);
+ })),
+ )
+ .child(
+ Button::new("invite-members", "Invite Members")
+ .selected(mode == Mode::InviteMembers)
+ .on_click(cx.listener(|this, _, cx| {
+ this.set_mode(Mode::InviteMembers, cx);
+ })),
+ ),
+ ),
+ )
+ .child(self.picker.clone())
}
-
- // fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
- // self.has_focus = true;
- // if cx.is_self_focused() {
- // cx.focus(&self.picker)
- // }
- // }
-
- // fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
- // self.has_focus = false;
- // }
}
#[derive(Copy, Clone, PartialEq)]
@@ -336,11 +245,11 @@ pub struct ChannelModalDelegate {
mode: Mode,
match_candidates: Vec<StringMatchCandidate>,
members: Vec<ChannelMembership>,
- // context_menu: ViewHandle<ContextMenu>,
+ context_menu: Option<(View<ContextMenu>, Subscription)>,
}
impl PickerDelegate for ChannelModalDelegate {
- type ListItem = Div;
+ type ListItem = ListItem;
fn placeholder_text(&self) -> Arc<str> {
"Search collaborator by username...".into()
@@ -420,11 +329,11 @@ impl PickerDelegate for ChannelModalDelegate {
if let Some((selected_user, role)) = self.user_at_index(self.selected_index) {
match self.mode {
Mode::ManageMembers => {
- self.show_context_menu(role.unwrap_or(ChannelRole::Member), cx)
+ self.show_context_menu(selected_user, role.unwrap_or(ChannelRole::Member), cx)
}
Mode::InviteMembers => match self.member_status(selected_user.id, cx) {
Some(proto::channel_member::Kind::Invitee) => {
- self.remove_selected_member(cx);
+ self.remove_member(selected_user.id, cx);
}
Some(proto::channel_member::Kind::AncestorMember) | None => {
self.invite_member(selected_user, cx)
@@ -436,11 +345,13 @@ impl PickerDelegate for ChannelModalDelegate {
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
- self.channel_modal
- .update(cx, |_, cx| {
- cx.emit(DismissEvent);
- })
- .ok();
+ if self.context_menu.is_none() {
+ self.channel_modal
+ .update(cx, |_, cx| {
+ cx.emit(DismissEvent);
+ })
+ .ok();
+ }
}
fn render_match(
@@ -449,129 +360,54 @@ impl PickerDelegate for ChannelModalDelegate {
selected: bool,
cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
- None
- // let full_theme = &theme::current(cx);
- // let theme = &full_theme.collab_panel.channel_modal;
- // let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
- // let (user, role) = self.user_at_index(ix).unwrap();
- // let request_status = self.member_status(user.id, cx);
-
- // let style = tabbed_modal
- // .picker
- // .item
- // .in_state(selected)
- // .style_for(mouse_state);
-
- // let in_manage = matches!(self.mode, Mode::ManageMembers);
-
- // let mut result = Flex::row()
- // .with_children(user.avatar.clone().map(|avatar| {
- // Image::from_data(avatar)
- // .with_style(theme.contact_avatar)
- // .aligned()
- // .left()
- // }))
- // .with_child(
- // Label::new(user.github_login.clone(), style.label.clone())
- // .contained()
- // .with_style(theme.contact_username)
- // .aligned()
- // .left(),
- // )
- // .with_children({
- // (in_manage && request_status == Some(proto::channel_member::Kind::Invitee)).then(
- // || {
- // Label::new("Invited", theme.member_tag.text.clone())
- // .contained()
- // .with_style(theme.member_tag.container)
- // .aligned()
- // .left()
- // },
- // )
- // })
- // .with_children(if in_manage && role == Some(ChannelRole::Admin) {
- // Some(
- // Label::new("Admin", theme.member_tag.text.clone())
- // .contained()
- // .with_style(theme.member_tag.container)
- // .aligned()
- // .left(),
- // )
- // } else if in_manage && role == Some(ChannelRole::Guest) {
- // Some(
- // Label::new("Guest", theme.member_tag.text.clone())
- // .contained()
- // .with_style(theme.member_tag.container)
- // .aligned()
- // .left(),
- // )
- // } else {
- // None
- // })
- // .with_children({
- // let svg = match self.mode {
- // Mode::ManageMembers => Some(
- // Svg::new("icons/ellipsis.svg")
- // .with_color(theme.member_icon.color)
- // .constrained()
- // .with_width(theme.member_icon.icon_width)
- // .aligned()
- // .constrained()
- // .with_width(theme.member_icon.button_width)
- // .with_height(theme.member_icon.button_width)
- // .contained()
- // .with_style(theme.member_icon.container),
- // ),
- // Mode::InviteMembers => match request_status {
- // Some(proto::channel_member::Kind::Member) => Some(
- // Svg::new("icons/check.svg")
- // .with_color(theme.member_icon.color)
- // .constrained()
- // .with_width(theme.member_icon.icon_width)
- // .aligned()
- // .constrained()
- // .with_width(theme.member_icon.button_width)
- // .with_height(theme.member_icon.button_width)
- // .contained()
- // .with_style(theme.member_icon.container),
- // ),
- // Some(proto::channel_member::Kind::Invitee) => Some(
- // Svg::new("icons/check.svg")
- // .with_color(theme.invitee_icon.color)
- // .constrained()
- // .with_width(theme.invitee_icon.icon_width)
- // .aligned()
- // .constrained()
- // .with_width(theme.invitee_icon.button_width)
- // .with_height(theme.invitee_icon.button_width)
- // .contained()
- // .with_style(theme.invitee_icon.container),
- // ),
- // Some(proto::channel_member::Kind::AncestorMember) | None => None,
- // },
- // };
-
- // svg.map(|svg| svg.aligned().flex_float().into_any())
- // })
- // .contained()
- // .with_style(style.container)
- // .constrained()
- // .with_height(tabbed_modal.row_height)
- // .into_any();
-
- // if selected {
- // result = Stack::new()
- // .with_child(result)
- // .with_child(
- // ChildView::new(&self.context_menu, cx)
- // .aligned()
- // .top()
- // .right(),
- // )
- // .into_any();
- // }
-
- // result
+ let (user, role) = self.user_at_index(ix)?;
+ let request_status = self.member_status(user.id, cx);
+
+ Some(
+ ListItem::new(ix)
+ .inset(true)
+ .selected(selected)
+ .start_slot(Avatar::new(user.avatar_uri.clone()))
+ .child(Label::new(user.github_login.clone()))
+ .end_slot(h_stack().gap_2().map(|slot| {
+ match self.mode {
+ Mode::ManageMembers => slot
+ .children(
+ if request_status == Some(proto::channel_member::Kind::Invitee) {
+ Some(Label::new("Invited"))
+ } else {
+ None
+ },
+ )
+ .children(match role {
+ Some(ChannelRole::Admin) => Some(Label::new("Admin")),
+ Some(ChannelRole::Guest) => Some(Label::new("Guest")),
+ _ => None,
+ })
+ .child(IconButton::new("ellipsis", Icon::Ellipsis))
+ .children(
+ if let (Some((menu, _)), true) = (&self.context_menu, selected) {
+ Some(
+ overlay()
+ .anchor(gpui::AnchorCorner::TopLeft)
+ .child(menu.clone()),
+ )
+ } else {
+ None
+ },
+ ),
+ Mode::InviteMembers => match request_status {
+ Some(proto::channel_member::Kind::Invitee) => {
+ slot.children(Some(Label::new("Invited")))
+ }
+ Some(proto::channel_member::Kind::Member) => {
+ slot.children(Some(Label::new("Member")))
+ }
+ _ => slot,
+ },
+ }
+ })),
+ )
}
}
@@ -605,21 +441,20 @@ impl ChannelModalDelegate {
}
}
- fn toggle_selected_member_admin(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
- let (user, role) = self.user_at_index(self.selected_index)?;
- let new_role = if role == Some(ChannelRole::Admin) {
- ChannelRole::Member
- } else {
- ChannelRole::Admin
- };
+ fn set_user_role(
+ &mut self,
+ user_id: UserId,
+ new_role: ChannelRole,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Option<()> {
let update = self.channel_store.update(cx, |store, cx| {
- store.set_member_role(self.channel_id, user.id, new_role, cx)
+ store.set_member_role(self.channel_id, user_id, new_role, cx)
});
cx.spawn(|picker, mut cx| async move {
update.await?;
picker.update(&mut cx, |picker, cx| {
let this = &mut picker.delegate;
- if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user.id) {
+ if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user_id) {
member.role = new_role;
}
cx.focus_self();
@@ -630,9 +465,7 @@ impl ChannelModalDelegate {
Some(())
}
- fn remove_selected_member(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
- let (user, _) = self.user_at_index(self.selected_index)?;
- let user_id = user.id;
+ fn remove_member(&mut self, user_id: UserId, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
let update = self.channel_store.update(cx, |store, cx| {
store.remove_member(self.channel_id, user_id, cx)
});
@@ -656,7 +489,7 @@ impl ChannelModalDelegate {
.selected_index
.min(this.matching_member_indices.len().saturating_sub(1));
- cx.focus_self();
+ picker.focus(cx);
cx.notify();
})
})
@@ -689,24 +522,55 @@ impl ChannelModalDelegate {
.detach_and_log_err(cx);
}
- fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) {
- // self.context_menu.update(cx, |context_menu, cx| {
- // context_menu.show(
- // Default::default(),
- // AnchorCorner::TopRight,
- // vec![
- // ContextMenuItem::action("Remove", RemoveMember),
- // ContextMenuItem::action(
- // if role == ChannelRole::Admin {
- // "Make non-admin"
- // } else {
- // "Make admin"
- // },
- // ToggleMemberAdmin,
- // ),
- // ],
- // cx,
- // )
- // })
+ fn show_context_menu(
+ &mut self,
+ user: Arc<User>,
+ role: ChannelRole,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) {
+ let user_id = user.id;
+ let picker = cx.view().clone();
+ let context_menu = ContextMenu::build(cx, |mut menu, _cx| {
+ menu = menu.entry("Remove Member", {
+ let picker = picker.clone();
+ move |cx| {
+ picker.update(cx, |picker, cx| {
+ picker.delegate.remove_member(user_id, cx);
+ })
+ }
+ });
+
+ let picker = picker.clone();
+ match role {
+ ChannelRole::Admin => {
+ menu = menu.entry("Revoke Admin", move |cx| {
+ picker.update(cx, |picker, cx| {
+ picker
+ .delegate
+ .set_user_role(user_id, ChannelRole::Member, cx);
+ })
+ });
+ }
+ ChannelRole::Member => {
+ menu = menu.entry("Make Admin", move |cx| {
+ picker.update(cx, |picker, cx| {
+ picker
+ .delegate
+ .set_user_role(user_id, ChannelRole::Admin, cx);
+ })
+ });
+ }
+ _ => {}
+ };
+
+ menu
+ });
+ cx.focus_view(&context_menu);
+ let subscription = cx.subscribe(&context_menu, |picker, _, _: &DismissEvent, cx| {
+ picker.delegate.context_menu = None;
+ picker.focus(cx);
+ cx.notify();
+ });
+ self.context_menu = Some((context_menu, subscription));
}
}