Implement channel modal
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
80b6922de7
commit
1c3698ae20
6 changed files with 213 additions and 243 deletions
|
@ -2167,13 +2167,13 @@ impl CollabPanel {
|
||||||
|
|
||||||
let controls = if is_incoming {
|
let controls = if is_incoming {
|
||||||
vec![
|
vec![
|
||||||
IconButton::new("remove_contact", Icon::Close)
|
IconButton::new("decline-contact", Icon::Close)
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.respond_to_contact_request(user_id, false, cx);
|
this.respond_to_contact_request(user_id, false, cx);
|
||||||
}))
|
}))
|
||||||
.icon_color(color)
|
.icon_color(color)
|
||||||
.tooltip(|cx| Tooltip::text("Decline invite", cx)),
|
.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| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.respond_to_contact_request(user_id, true, cx);
|
this.respond_to_contact_request(user_id, true, cx);
|
||||||
}))
|
}))
|
||||||
|
@ -2220,15 +2220,15 @@ impl CollabPanel {
|
||||||
};
|
};
|
||||||
|
|
||||||
let controls = [
|
let controls = [
|
||||||
IconButton::new("remove_contact", Icon::Close)
|
IconButton::new("reject-invite", Icon::Close)
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.respond_to_channel_invite(channel_id, false, cx);
|
this.respond_to_channel_invite(channel_id, false, cx);
|
||||||
}))
|
}))
|
||||||
.icon_color(color)
|
.icon_color(color)
|
||||||
.tooltip(|cx| Tooltip::text("Decline invite", cx)),
|
.tooltip(|cx| Tooltip::text("Decline invite", cx)),
|
||||||
IconButton::new("remove_contact", Icon::Check)
|
IconButton::new("accept-invite", Icon::Check)
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.respond_to_contact_request(channel_id, true, cx);
|
this.respond_to_channel_invite(channel_id, true, cx);
|
||||||
}))
|
}))
|
||||||
.icon_color(color)
|
.icon_color(color)
|
||||||
.tooltip(|cx| Tooltip::text("Accept invite", cx)),
|
.tooltip(|cx| Tooltip::text("Accept invite", cx)),
|
||||||
|
|
|
@ -5,13 +5,13 @@ use client::{
|
||||||
};
|
};
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, AppContext, ClipboardItem, DismissEvent, Div, Entity, EventEmitter,
|
actions, div, overlay, AppContext, ClipboardItem, DismissEvent, Div, EventEmitter,
|
||||||
FocusableView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext,
|
FocusableView, Model, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
|
||||||
WeakView,
|
VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::{prelude::*, Checkbox};
|
use ui::{prelude::*, Avatar, Checkbox, ContextMenu, ListItem};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
use workspace::ModalView;
|
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 {
|
pub struct ChannelModal {
|
||||||
picker: View<Picker<ChannelModalDelegate>>,
|
picker: View<Picker<ChannelModalDelegate>>,
|
||||||
channel_store: Model<ChannelStore>,
|
channel_store: Model<ChannelStore>,
|
||||||
channel_id: ChannelId,
|
channel_id: ChannelId,
|
||||||
has_focus: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelModal {
|
impl ChannelModal {
|
||||||
|
@ -62,25 +53,19 @@ impl ChannelModal {
|
||||||
channel_store: channel_store.clone(),
|
channel_store: channel_store.clone(),
|
||||||
channel_id,
|
channel_id,
|
||||||
match_candidates: Vec::new(),
|
match_candidates: Vec::new(),
|
||||||
|
context_menu: None,
|
||||||
members,
|
members,
|
||||||
mode,
|
mode,
|
||||||
// context_menu: cx.add_view(|cx| {
|
|
||||||
// let mut menu = ContextMenu::new(cx.view_id(), cx);
|
|
||||||
// menu.set_position_mode(OverlayPositionMode::Local);
|
|
||||||
// menu
|
|
||||||
// }),
|
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
.modal(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
let has_focus = picker.focus_handle(cx).contains_focused(cx);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
picker,
|
picker,
|
||||||
channel_store,
|
channel_store,
|
||||||
channel_id,
|
channel_id,
|
||||||
has_focus,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,15 +111,19 @@ impl ChannelModal {
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_member_admin(&mut self, _: &ToggleMemberAdmin, cx: &mut ViewContext<Self>) {
|
fn set_channel_visiblity(&mut self, selection: &Selection, cx: &mut ViewContext<Self>) {
|
||||||
self.picker.update(cx, |picker, cx| {
|
self.channel_store.update(cx, |channel_store, cx| {
|
||||||
picker.delegate.toggle_selected_member_admin(cx);
|
channel_store
|
||||||
})
|
.set_channel_visibility(
|
||||||
}
|
self.channel_id,
|
||||||
|
match selection {
|
||||||
fn remove_member(&mut self, _: &RemoveMember, cx: &mut ViewContext<Self>) {
|
Selection::Unselected => ChannelVisibility::Members,
|
||||||
self.picker.update(cx, |picker, cx| {
|
Selection::Selected => ChannelVisibility::Public,
|
||||||
picker.delegate.remove_selected_member(cx);
|
Selection::Indeterminate => return,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.detach_and_log_err(cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,50 +149,80 @@ impl Render for ChannelModal {
|
||||||
let Some(channel) = channel_store.channel_for_id(self.channel_id) else {
|
let Some(channel) = channel_store.channel_for_id(self.channel_id) else {
|
||||||
return div();
|
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;
|
let mode = self.picker.read(cx).delegate.mode;
|
||||||
|
|
||||||
v_stack()
|
v_stack()
|
||||||
.bg(cx.theme().colors().elevated_surface_background)
|
.key_context("ChannelModal")
|
||||||
|
.on_action(cx.listener(Self::toggle_mode))
|
||||||
|
.on_action(cx.listener(Self::dismiss))
|
||||||
|
.elevation_3(cx)
|
||||||
.w(rems(34.))
|
.w(rems(34.))
|
||||||
.child(Label::new(channel.name.clone()))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
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()
|
.w_full()
|
||||||
.flex_row()
|
.justify_between()
|
||||||
.child(Checkbox::new(
|
.child(
|
||||||
|
h_stack()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
Checkbox::new(
|
||||||
"is-public",
|
"is-public",
|
||||||
if channel.visibility == ChannelVisibility::Public {
|
if visibility == ChannelVisibility::Public {
|
||||||
ui::Selection::Selected
|
ui::Selection::Selected
|
||||||
} else {
|
} else {
|
||||||
ui::Selection::Unselected
|
ui::Selection::Unselected
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
.on_click(cx.listener(Self::set_channel_visiblity)),
|
||||||
|
)
|
||||||
.child(Label::new("Public")),
|
.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(
|
.child(
|
||||||
div()
|
div()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
.flex()
|
||||||
.flex_row()
|
.flex_row()
|
||||||
.child(
|
.child(
|
||||||
Button::new("manage-members", "Manage Members")
|
Button::new("manage-members", "Manage Members")
|
||||||
.selected(mode == Mode::ManageMembers)
|
.selected(mode == Mode::ManageMembers)
|
||||||
.on_click(cx.listener(|this, _, cx| {
|
.on_click(cx.listener(|this, _, cx| {
|
||||||
this.picker.update(cx, |picker, _| {
|
this.set_mode(Mode::ManageMembers, cx);
|
||||||
picker.delegate.mode = Mode::ManageMembers
|
|
||||||
});
|
|
||||||
cx.notify();
|
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("invite-members", "Invite Members")
|
Button::new("invite-members", "Invite Members")
|
||||||
.selected(mode == Mode::InviteMembers)
|
.selected(mode == Mode::InviteMembers)
|
||||||
.on_click(cx.listener(|this, _, cx| {
|
.on_click(cx.listener(|this, _, cx| {
|
||||||
this.picker.update(cx, |picker, _| {
|
this.set_mode(Mode::InviteMembers, cx);
|
||||||
picker.delegate.mode = Mode::InviteMembers
|
|
||||||
});
|
|
||||||
cx.notify();
|
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(self.picker.clone())
|
.child(self.picker.clone())
|
||||||
}
|
}
|
||||||
|
@ -226,11 +245,11 @@ pub struct ChannelModalDelegate {
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
match_candidates: Vec<StringMatchCandidate>,
|
match_candidates: Vec<StringMatchCandidate>,
|
||||||
members: Vec<ChannelMembership>,
|
members: Vec<ChannelMembership>,
|
||||||
// context_menu: ViewHandle<ContextMenu>,
|
context_menu: Option<(View<ContextMenu>, Subscription)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for ChannelModalDelegate {
|
impl PickerDelegate for ChannelModalDelegate {
|
||||||
type ListItem = Div;
|
type ListItem = ListItem;
|
||||||
|
|
||||||
fn placeholder_text(&self) -> Arc<str> {
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
"Search collaborator by username...".into()
|
"Search collaborator by username...".into()
|
||||||
|
@ -310,11 +329,11 @@ impl PickerDelegate for ChannelModalDelegate {
|
||||||
if let Some((selected_user, role)) = self.user_at_index(self.selected_index) {
|
if let Some((selected_user, role)) = self.user_at_index(self.selected_index) {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::ManageMembers => {
|
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) {
|
Mode::InviteMembers => match self.member_status(selected_user.id, cx) {
|
||||||
Some(proto::channel_member::Kind::Invitee) => {
|
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 => {
|
Some(proto::channel_member::Kind::AncestorMember) | None => {
|
||||||
self.invite_member(selected_user, cx)
|
self.invite_member(selected_user, cx)
|
||||||
|
@ -326,12 +345,14 @@ impl PickerDelegate for ChannelModalDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
if self.context_menu.is_none() {
|
||||||
self.channel_modal
|
self.channel_modal
|
||||||
.update(cx, |_, cx| {
|
.update(cx, |_, cx| {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
&self,
|
&self,
|
||||||
|
@ -339,129 +360,54 @@ impl PickerDelegate for ChannelModalDelegate {
|
||||||
selected: bool,
|
selected: bool,
|
||||||
cx: &mut ViewContext<Picker<Self>>,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
) -> Option<Self::ListItem> {
|
) -> Option<Self::ListItem> {
|
||||||
|
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
|
None
|
||||||
// let full_theme = &theme::current(cx);
|
},
|
||||||
// let theme = &full_theme.collab_panel.channel_modal;
|
)
|
||||||
// let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
|
.children(match role {
|
||||||
// let (user, role) = self.user_at_index(ix).unwrap();
|
Some(ChannelRole::Admin) => Some(Label::new("Admin")),
|
||||||
// let request_status = self.member_status(user.id, cx);
|
Some(ChannelRole::Guest) => Some(Label::new("Guest")),
|
||||||
|
_ => None,
|
||||||
// let style = tabbed_modal
|
})
|
||||||
// .picker
|
.child(IconButton::new("ellipsis", Icon::Ellipsis))
|
||||||
// .item
|
.children(
|
||||||
// .in_state(selected)
|
if let (Some((menu, _)), true) = (&self.context_menu, selected) {
|
||||||
// .style_for(mouse_state);
|
Some(
|
||||||
|
overlay()
|
||||||
// let in_manage = matches!(self.mode, Mode::ManageMembers);
|
.anchor(gpui::AnchorCorner::TopLeft)
|
||||||
|
.child(menu.clone()),
|
||||||
// let mut result = Flex::row()
|
)
|
||||||
// .with_children(user.avatar.clone().map(|avatar| {
|
} else {
|
||||||
// Image::from_data(avatar)
|
None
|
||||||
// .with_style(theme.contact_avatar)
|
},
|
||||||
// .aligned()
|
),
|
||||||
// .left()
|
Mode::InviteMembers => match request_status {
|
||||||
// }))
|
Some(proto::channel_member::Kind::Invitee) => {
|
||||||
// .with_child(
|
slot.children(Some(Label::new("Invited")))
|
||||||
// Label::new(user.github_login.clone(), style.label.clone())
|
}
|
||||||
// .contained()
|
Some(proto::channel_member::Kind::Member) => {
|
||||||
// .with_style(theme.contact_username)
|
slot.children(Some(Label::new("Member")))
|
||||||
// .aligned()
|
}
|
||||||
// .left(),
|
_ => slot,
|
||||||
// )
|
},
|
||||||
// .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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,21 +441,20 @@ impl ChannelModalDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_selected_member_admin(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
|
fn set_user_role(
|
||||||
let (user, role) = self.user_at_index(self.selected_index)?;
|
&mut self,
|
||||||
let new_role = if role == Some(ChannelRole::Admin) {
|
user_id: UserId,
|
||||||
ChannelRole::Member
|
new_role: ChannelRole,
|
||||||
} else {
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
ChannelRole::Admin
|
) -> Option<()> {
|
||||||
};
|
|
||||||
let update = self.channel_store.update(cx, |store, cx| {
|
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 {
|
cx.spawn(|picker, mut cx| async move {
|
||||||
update.await?;
|
update.await?;
|
||||||
picker.update(&mut cx, |picker, cx| {
|
picker.update(&mut cx, |picker, cx| {
|
||||||
let this = &mut picker.delegate;
|
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;
|
member.role = new_role;
|
||||||
}
|
}
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
|
@ -520,9 +465,7 @@ impl ChannelModalDelegate {
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_selected_member(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
|
fn remove_member(&mut self, user_id: UserId, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
|
||||||
let (user, _) = self.user_at_index(self.selected_index)?;
|
|
||||||
let user_id = user.id;
|
|
||||||
let update = self.channel_store.update(cx, |store, cx| {
|
let update = self.channel_store.update(cx, |store, cx| {
|
||||||
store.remove_member(self.channel_id, user_id, cx)
|
store.remove_member(self.channel_id, user_id, cx)
|
||||||
});
|
});
|
||||||
|
@ -546,7 +489,7 @@ impl ChannelModalDelegate {
|
||||||
.selected_index
|
.selected_index
|
||||||
.min(this.matching_member_indices.len().saturating_sub(1));
|
.min(this.matching_member_indices.len().saturating_sub(1));
|
||||||
|
|
||||||
cx.focus_self();
|
picker.focus(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -579,24 +522,55 @@ impl ChannelModalDelegate {
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) {
|
fn show_context_menu(
|
||||||
// self.context_menu.update(cx, |context_menu, cx| {
|
&mut self,
|
||||||
// context_menu.show(
|
user: Arc<User>,
|
||||||
// Default::default(),
|
role: ChannelRole,
|
||||||
// AnchorCorner::TopRight,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
// vec![
|
) {
|
||||||
// ContextMenuItem::action("Remove", RemoveMember),
|
let user_id = user.id;
|
||||||
// ContextMenuItem::action(
|
let picker = cx.view().clone();
|
||||||
// if role == ChannelRole::Admin {
|
let context_menu = ContextMenu::build(cx, |mut menu, _cx| {
|
||||||
// "Make non-admin"
|
menu = menu.entry("Remove Member", {
|
||||||
// } else {
|
let picker = picker.clone();
|
||||||
// "Make admin"
|
move |cx| {
|
||||||
// },
|
picker.update(cx, |picker, cx| {
|
||||||
// ToggleMemberAdmin,
|
picker.delegate.remove_member(user_id, cx);
|
||||||
// ),
|
})
|
||||||
// ],
|
}
|
||||||
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2662,13 +2662,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||||
self.defer(|view, cx| view.focus_handle(cx).focus(cx))
|
self.defer(|view, cx| view.focus_handle(cx).focus(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dismiss_self(&mut self)
|
|
||||||
where
|
|
||||||
V: ManagedView,
|
|
||||||
{
|
|
||||||
self.defer(|_, cx| cx.emit(DismissEvent))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listener<E>(
|
pub fn listener<E>(
|
||||||
&self,
|
&self,
|
||||||
f: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
|
f: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
|
||||||
|
|
|
@ -239,7 +239,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
||||||
);
|
);
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.key_context("picker")
|
.key_context("Picker")
|
||||||
.size_full()
|
.size_full()
|
||||||
.when_some(self.width, |el, width| {
|
.when_some(self.width, |el, width| {
|
||||||
el.w(width)
|
el.w(width)
|
||||||
|
|
|
@ -72,11 +72,11 @@ impl ContextMenu {
|
||||||
pub fn entry(
|
pub fn entry(
|
||||||
mut self,
|
mut self,
|
||||||
label: impl Into<SharedString>,
|
label: impl Into<SharedString>,
|
||||||
on_click: impl Fn(&mut WindowContext) + 'static,
|
handler: impl Fn(&mut WindowContext) + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.items.push(ContextMenuItem::Entry {
|
self.items.push(ContextMenuItem::Entry {
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
handler: Rc::new(on_click),
|
handler: Rc::new(handler),
|
||||||
icon: None,
|
icon: None,
|
||||||
action: None,
|
action: None,
|
||||||
});
|
});
|
||||||
|
@ -114,6 +114,7 @@ impl ContextMenu {
|
||||||
|
|
||||||
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
|
cx.emit(DismissEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
|
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ pub enum Icon {
|
||||||
CopilotDisabled,
|
CopilotDisabled,
|
||||||
Dash,
|
Dash,
|
||||||
Disconnected,
|
Disconnected,
|
||||||
|
Ellipsis,
|
||||||
Envelope,
|
Envelope,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
ExclamationTriangle,
|
ExclamationTriangle,
|
||||||
|
@ -133,6 +134,7 @@ impl Icon {
|
||||||
Icon::CopilotDisabled => "icons/copilot_disabled.svg",
|
Icon::CopilotDisabled => "icons/copilot_disabled.svg",
|
||||||
Icon::Dash => "icons/dash.svg",
|
Icon::Dash => "icons/dash.svg",
|
||||||
Icon::Disconnected => "icons/disconnected.svg",
|
Icon::Disconnected => "icons/disconnected.svg",
|
||||||
|
Icon::Ellipsis => "icons/ellipsis.svg",
|
||||||
Icon::Envelope => "icons/feedback.svg",
|
Icon::Envelope => "icons/feedback.svg",
|
||||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
Icon::ExclamationTriangle => "icons/warning.svg",
|
||||||
Icon::ExternalLink => "icons/external_link.svg",
|
Icon::ExternalLink => "icons/external_link.svg",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue