Get ChannelModal opening

This commit is contained in:
Conrad Irwin 2023-12-02 00:57:41 -07:00
parent 63b65b2b2e
commit 859f2d2862
4 changed files with 403 additions and 406 deletions

View file

@ -1,5 +1,5 @@
#![allow(unused)] #![allow(unused)]
// mod channel_modal; mod channel_modal;
mod contact_finder; mod contact_finder;
// use crate::{ // use crate::{
@ -192,6 +192,8 @@ use workspace::{
use crate::{face_pile::FacePile, CollaborationPanelSettings}; use crate::{face_pile::FacePile, CollaborationPanelSettings};
use self::channel_modal::ChannelModal;
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, _| { cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| { workspace.register_action(|workspace, _: &ToggleFocus, cx| {
@ -2058,13 +2060,11 @@ impl CollabPanel {
} }
fn invite_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) { fn invite_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
todo!(); self.show_channel_modal(channel_id, channel_modal::Mode::InviteMembers, cx);
// self.show_channel_modal(channel_id, channel_modal::Mode::InviteMembers, cx);
} }
fn manage_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) { fn manage_members(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
todo!(); self.show_channel_modal(channel_id, channel_modal::Mode::ManageMembers, cx);
// self.show_channel_modal(channel_id, channel_modal::Mode::ManageMembers, cx);
} }
fn remove_selected_channel(&mut self, _: &Remove, cx: &mut ViewContext<Self>) { fn remove_selected_channel(&mut self, _: &Remove, cx: &mut ViewContext<Self>) {
@ -2156,38 +2156,36 @@ impl CollabPanel {
}) })
} }
// fn show_channel_modal( fn show_channel_modal(
// &mut self, &mut self,
// channel_id: ChannelId, channel_id: ChannelId,
// mode: channel_modal::Mode, mode: channel_modal::Mode,
// cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
// ) { ) {
// let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
// let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
// let channel_store = self.channel_store.clone(); let channel_store = self.channel_store.clone();
// let members = self.channel_store.update(cx, |channel_store, cx| { let members = self.channel_store.update(cx, |channel_store, cx| {
// channel_store.get_channel_member_details(channel_id, cx) channel_store.get_channel_member_details(channel_id, cx)
// }); });
// cx.spawn(|_, mut cx| async move { cx.spawn(|_, mut cx| async move {
// let members = members.await?; let members = members.await?;
// workspace.update(&mut cx, |workspace, cx| { workspace.update(&mut cx, |workspace, cx| {
// workspace.toggle_modal(cx, |_, cx| { workspace.toggle_modal(cx, |cx| {
// cx.add_view(|cx| { ChannelModal::new(
// ChannelModal::new( user_store.clone(),
// user_store.clone(), channel_store.clone(),
// channel_store.clone(), channel_id,
// channel_id, mode,
// mode, members,
// members, cx,
// cx, )
// ) });
// }) })
// }); })
// }) .detach();
// }) }
// .detach();
// }
// fn remove_selected_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) { // fn remove_selected_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
// self.remove_channel(action.channel_id, cx) // self.remove_channel(action.channel_id, cx)

View file

@ -3,58 +3,54 @@ use client::{
proto::{self, ChannelRole, ChannelVisibility}, proto::{self, ChannelRole, ChannelVisibility},
User, UserId, UserStore, User, UserId, UserStore,
}; };
use context_menu::{ContextMenu, ContextMenuItem};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{ use gpui::{
actions, actions, div, AppContext, ClipboardItem, DismissEvent, Div, Entity, EventEmitter,
elements::*, FocusableView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext,
platform::{CursorStyle, MouseButton}, WeakView,
AppContext, ClipboardItem, Entity, ModelHandle, MouseState, Task, View, ViewContext,
ViewHandle,
}; };
use picker::{Picker, PickerDelegate, PickerEvent}; use picker::{Picker, PickerDelegate};
use std::sync::Arc; use std::sync::Arc;
use ui::v_stack;
use util::TryFutureExt; use util::TryFutureExt;
use workspace::Modal;
actions!( actions!(
channel_modal, SelectNextControl,
[ ToggleMode,
SelectNextControl, ToggleMemberAdmin,
ToggleMode, RemoveMember
ToggleMemberAdmin,
RemoveMember
]
); );
pub fn init(cx: &mut AppContext) { // pub fn init(cx: &mut AppContext) {
Picker::<ChannelModalDelegate>::init(cx); // Picker::<ChannelModalDelegate>::init(cx);
cx.add_action(ChannelModal::toggle_mode); // cx.add_action(ChannelModal::toggle_mode);
cx.add_action(ChannelModal::toggle_member_admin); // cx.add_action(ChannelModal::toggle_member_admin);
cx.add_action(ChannelModal::remove_member); // cx.add_action(ChannelModal::remove_member);
cx.add_action(ChannelModal::dismiss); // cx.add_action(ChannelModal::dismiss);
} // }
pub struct ChannelModal { pub struct ChannelModal {
picker: ViewHandle<Picker<ChannelModalDelegate>>, picker: View<Picker<ChannelModalDelegate>>,
channel_store: ModelHandle<ChannelStore>, channel_store: Model<ChannelStore>,
channel_id: ChannelId, channel_id: ChannelId,
has_focus: bool, has_focus: bool,
} }
impl ChannelModal { impl ChannelModal {
pub fn new( pub fn new(
user_store: ModelHandle<UserStore>, user_store: Model<UserStore>,
channel_store: ModelHandle<ChannelStore>, channel_store: Model<ChannelStore>,
channel_id: ChannelId, channel_id: ChannelId,
mode: Mode, mode: Mode,
members: Vec<ChannelMembership>, members: Vec<ChannelMembership>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
cx.observe(&channel_store, |_, _, cx| cx.notify()).detach(); cx.observe(&channel_store, |_, _, cx| cx.notify()).detach();
let picker = cx.add_view(|cx| { let channel_modal = cx.view().downgrade();
let picker = cx.build_view(|cx| {
Picker::new( Picker::new(
ChannelModalDelegate { ChannelModalDelegate {
channel_modal,
matching_users: Vec::new(), matching_users: Vec::new(),
matching_member_indices: Vec::new(), matching_member_indices: Vec::new(),
selected_index: 0, selected_index: 0,
@ -64,20 +60,17 @@ impl ChannelModal {
match_candidates: Vec::new(), match_candidates: Vec::new(),
members, members,
mode, mode,
context_menu: cx.add_view(|cx| { // context_menu: cx.add_view(|cx| {
let mut menu = ContextMenu::new(cx.view_id(), cx); // let mut menu = ContextMenu::new(cx.view_id(), cx);
menu.set_position_mode(OverlayPositionMode::Local); // menu.set_position_mode(OverlayPositionMode::Local);
menu // menu
}), // }),
}, },
cx, cx,
) )
.with_theme(|theme| theme.collab_panel.tabbed_modal.picker.clone())
}); });
cx.subscribe(&picker, |_, _, e, cx| cx.emit(*e)).detach(); let has_focus = picker.focus_handle(cx).contains_focused(cx);
let has_focus = picker.read(cx).has_focus();
Self { Self {
picker, picker,
@ -88,7 +81,7 @@ impl ChannelModal {
} }
fn toggle_mode(&mut self, _: &ToggleMode, cx: &mut ViewContext<Self>) { fn toggle_mode(&mut self, _: &ToggleMode, cx: &mut ViewContext<Self>) {
let mode = match self.picker.read(cx).delegate().mode { let mode = match self.picker.read(cx).delegate.mode {
Mode::ManageMembers => Mode::InviteMembers, Mode::ManageMembers => Mode::InviteMembers,
Mode::InviteMembers => Mode::ManageMembers, Mode::InviteMembers => Mode::ManageMembers,
}; };
@ -103,20 +96,20 @@ impl ChannelModal {
let mut members = channel_store let mut members = channel_store
.update(&mut cx, |channel_store, cx| { .update(&mut cx, |channel_store, cx| {
channel_store.get_channel_member_details(channel_id, cx) channel_store.get_channel_member_details(channel_id, cx)
}) })?
.await?; .await?;
members.sort_by(|a, b| a.sort_key().cmp(&b.sort_key())); members.sort_by(|a, b| a.sort_key().cmp(&b.sort_key()));
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.picker this.picker
.update(cx, |picker, _| picker.delegate_mut().members = members); .update(cx, |picker, _| picker.delegate.members = members);
})?; })?;
} }
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.picker.update(cx, |picker, cx| { this.picker.update(cx, |picker, cx| {
let delegate = picker.delegate_mut(); let delegate = &mut picker.delegate;
delegate.mode = mode; delegate.mode = mode;
delegate.selected_index = 0; delegate.selected_index = 0;
picker.set_query("", cx); picker.set_query("", cx);
@ -131,203 +124,194 @@ impl ChannelModal {
fn toggle_member_admin(&mut self, _: &ToggleMemberAdmin, cx: &mut ViewContext<Self>) { fn toggle_member_admin(&mut self, _: &ToggleMemberAdmin, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| { self.picker.update(cx, |picker, cx| {
picker.delegate_mut().toggle_selected_member_admin(cx); picker.delegate.toggle_selected_member_admin(cx);
}) })
} }
fn remove_member(&mut self, _: &RemoveMember, cx: &mut ViewContext<Self>) { fn remove_member(&mut self, _: &RemoveMember, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| { self.picker.update(cx, |picker, cx| {
picker.delegate_mut().remove_selected_member(cx); picker.delegate.remove_selected_member(cx);
}); });
} }
fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) { fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(PickerEvent::Dismiss); cx.emit(DismissEvent);
} }
} }
impl Entity for ChannelModal { impl EventEmitter<DismissEvent> for ChannelModal {}
type Event = PickerEvent;
}
impl View for ChannelModal { impl FocusableView for ChannelModal {
fn ui_name() -> &'static str { fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
"ChannelModal" self.picker.focus_handle(cx)
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
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()
}
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;
} }
} }
impl Modal for ChannelModal { impl Render for ChannelModal {
fn has_focus(&self) -> bool { type Element = Div;
self.has_focus
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
v_stack().min_w_96().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()
} }
fn dismiss_on_event(event: &Self::Event) -> bool { // fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
match event { // self.has_focus = true;
PickerEvent::Dismiss => 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)] #[derive(Copy, Clone, PartialEq)]
@ -337,19 +321,22 @@ pub enum Mode {
} }
pub struct ChannelModalDelegate { pub struct ChannelModalDelegate {
channel_modal: WeakView<ChannelModal>,
matching_users: Vec<Arc<User>>, matching_users: Vec<Arc<User>>,
matching_member_indices: Vec<usize>, matching_member_indices: Vec<usize>,
user_store: ModelHandle<UserStore>, user_store: Model<UserStore>,
channel_store: ModelHandle<ChannelStore>, channel_store: Model<ChannelStore>,
channel_id: ChannelId, channel_id: ChannelId,
selected_index: usize, selected_index: usize,
mode: Mode, mode: Mode,
match_candidates: Vec<StringMatchCandidate>, match_candidates: Vec<StringMatchCandidate>,
members: Vec<ChannelMembership>, members: Vec<ChannelMembership>,
context_menu: ViewHandle<ContextMenu>, // context_menu: ViewHandle<ContextMenu>,
} }
impl PickerDelegate for ChannelModalDelegate { impl PickerDelegate for ChannelModalDelegate {
type ListItem = Div;
fn placeholder_text(&self) -> Arc<str> { fn placeholder_text(&self) -> Arc<str> {
"Search collaborator by username...".into() "Search collaborator by username...".into()
} }
@ -382,19 +369,19 @@ impl PickerDelegate for ChannelModalDelegate {
} }
})); }));
let matches = cx.background().block(match_strings( let matches = cx.background_executor().block(match_strings(
&self.match_candidates, &self.match_candidates,
&query, &query,
true, true,
usize::MAX, usize::MAX,
&Default::default(), &Default::default(),
cx.background().clone(), cx.background_executor().clone(),
)); ));
cx.spawn(|picker, mut cx| async move { cx.spawn(|picker, mut cx| async move {
picker picker
.update(&mut cx, |picker, cx| { .update(&mut cx, |picker, cx| {
let delegate = picker.delegate_mut(); let delegate = &mut picker.delegate;
delegate.matching_member_indices.clear(); delegate.matching_member_indices.clear();
delegate delegate
.matching_member_indices .matching_member_indices
@ -412,8 +399,7 @@ impl PickerDelegate for ChannelModalDelegate {
async { async {
let users = search_users.await?; let users = search_users.await?;
picker.update(&mut cx, |picker, cx| { picker.update(&mut cx, |picker, cx| {
let delegate = picker.delegate_mut(); picker.delegate.matching_users = users;
delegate.matching_users = users;
cx.notify(); cx.notify();
})?; })?;
anyhow::Ok(()) anyhow::Ok(())
@ -445,138 +431,142 @@ impl PickerDelegate for ChannelModalDelegate {
} }
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) { fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
cx.emit(PickerEvent::Dismiss); self.channel_modal
.update(cx, |_, cx| {
cx.emit(DismissEvent);
})
.ok();
} }
fn render_match( fn render_match(
&self, &self,
ix: usize, ix: usize,
mouse_state: &mut MouseState,
selected: bool, selected: bool,
cx: &gpui::AppContext, cx: &mut ViewContext<Picker<Self>>,
) -> AnyElement<Picker<Self>> { ) -> Option<Self::ListItem> {
let full_theme = &theme::current(cx); None
let theme = &full_theme.collab_panel.channel_modal; // let full_theme = &theme::current(cx);
let tabbed_modal = &full_theme.collab_panel.tabbed_modal; // let theme = &full_theme.collab_panel.channel_modal;
let (user, role) = self.user_at_index(ix).unwrap(); // let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
let request_status = self.member_status(user.id, cx); // let (user, role) = self.user_at_index(ix).unwrap();
// let request_status = self.member_status(user.id, cx);
let style = tabbed_modal // let style = tabbed_modal
.picker // .picker
.item // .item
.in_state(selected) // .in_state(selected)
.style_for(mouse_state); // .style_for(mouse_state);
let in_manage = matches!(self.mode, Mode::ManageMembers); // let in_manage = matches!(self.mode, Mode::ManageMembers);
let mut result = Flex::row() // let mut result = Flex::row()
.with_children(user.avatar.clone().map(|avatar| { // .with_children(user.avatar.clone().map(|avatar| {
Image::from_data(avatar) // Image::from_data(avatar)
.with_style(theme.contact_avatar) // .with_style(theme.contact_avatar)
.aligned() // .aligned()
.left() // .left()
})) // }))
.with_child( // .with_child(
Label::new(user.github_login.clone(), style.label.clone()) // Label::new(user.github_login.clone(), style.label.clone())
.contained() // .contained()
.with_style(theme.contact_username) // .with_style(theme.contact_username)
.aligned() // .aligned()
.left(), // .left(),
) // )
.with_children({ // .with_children({
(in_manage && request_status == Some(proto::channel_member::Kind::Invitee)).then( // (in_manage && request_status == Some(proto::channel_member::Kind::Invitee)).then(
|| { // || {
Label::new("Invited", theme.member_tag.text.clone()) // Label::new("Invited", theme.member_tag.text.clone())
.contained() // .contained()
.with_style(theme.member_tag.container) // .with_style(theme.member_tag.container)
.aligned() // .aligned()
.left() // .left()
}, // },
) // )
}) // })
.with_children(if in_manage && role == Some(ChannelRole::Admin) { // .with_children(if in_manage && role == Some(ChannelRole::Admin) {
Some( // Some(
Label::new("Admin", theme.member_tag.text.clone()) // Label::new("Admin", theme.member_tag.text.clone())
.contained() // .contained()
.with_style(theme.member_tag.container) // .with_style(theme.member_tag.container)
.aligned() // .aligned()
.left(), // .left(),
) // )
} else if in_manage && role == Some(ChannelRole::Guest) { // } else if in_manage && role == Some(ChannelRole::Guest) {
Some( // Some(
Label::new("Guest", theme.member_tag.text.clone()) // Label::new("Guest", theme.member_tag.text.clone())
.contained() // .contained()
.with_style(theme.member_tag.container) // .with_style(theme.member_tag.container)
.aligned() // .aligned()
.left(), // .left(),
) // )
} else { // } else {
None // None
}) // })
.with_children({ // .with_children({
let svg = match self.mode { // let svg = match self.mode {
Mode::ManageMembers => Some( // Mode::ManageMembers => Some(
Svg::new("icons/ellipsis.svg") // Svg::new("icons/ellipsis.svg")
.with_color(theme.member_icon.color) // .with_color(theme.member_icon.color)
.constrained() // .constrained()
.with_width(theme.member_icon.icon_width) // .with_width(theme.member_icon.icon_width)
.aligned() // .aligned()
.constrained() // .constrained()
.with_width(theme.member_icon.button_width) // .with_width(theme.member_icon.button_width)
.with_height(theme.member_icon.button_width) // .with_height(theme.member_icon.button_width)
.contained() // .contained()
.with_style(theme.member_icon.container), // .with_style(theme.member_icon.container),
), // ),
Mode::InviteMembers => match request_status { // Mode::InviteMembers => match request_status {
Some(proto::channel_member::Kind::Member) => Some( // Some(proto::channel_member::Kind::Member) => Some(
Svg::new("icons/check.svg") // Svg::new("icons/check.svg")
.with_color(theme.member_icon.color) // .with_color(theme.member_icon.color)
.constrained() // .constrained()
.with_width(theme.member_icon.icon_width) // .with_width(theme.member_icon.icon_width)
.aligned() // .aligned()
.constrained() // .constrained()
.with_width(theme.member_icon.button_width) // .with_width(theme.member_icon.button_width)
.with_height(theme.member_icon.button_width) // .with_height(theme.member_icon.button_width)
.contained() // .contained()
.with_style(theme.member_icon.container), // .with_style(theme.member_icon.container),
), // ),
Some(proto::channel_member::Kind::Invitee) => Some( // Some(proto::channel_member::Kind::Invitee) => Some(
Svg::new("icons/check.svg") // Svg::new("icons/check.svg")
.with_color(theme.invitee_icon.color) // .with_color(theme.invitee_icon.color)
.constrained() // .constrained()
.with_width(theme.invitee_icon.icon_width) // .with_width(theme.invitee_icon.icon_width)
.aligned() // .aligned()
.constrained() // .constrained()
.with_width(theme.invitee_icon.button_width) // .with_width(theme.invitee_icon.button_width)
.with_height(theme.invitee_icon.button_width) // .with_height(theme.invitee_icon.button_width)
.contained() // .contained()
.with_style(theme.invitee_icon.container), // .with_style(theme.invitee_icon.container),
), // ),
Some(proto::channel_member::Kind::AncestorMember) | None => None, // Some(proto::channel_member::Kind::AncestorMember) | None => None,
}, // },
}; // };
svg.map(|svg| svg.aligned().flex_float().into_any()) // svg.map(|svg| svg.aligned().flex_float().into_any())
}) // })
.contained() // .contained()
.with_style(style.container) // .with_style(style.container)
.constrained() // .constrained()
.with_height(tabbed_modal.row_height) // .with_height(tabbed_modal.row_height)
.into_any(); // .into_any();
if selected { // if selected {
result = Stack::new() // result = Stack::new()
.with_child(result) // .with_child(result)
.with_child( // .with_child(
ChildView::new(&self.context_menu, cx) // ChildView::new(&self.context_menu, cx)
.aligned() // .aligned()
.top() // .top()
.right(), // .right(),
) // )
.into_any(); // .into_any();
} // }
result // result
} }
} }
@ -623,7 +613,7 @@ impl ChannelModalDelegate {
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 = picker.delegate_mut(); 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;
} }
@ -644,7 +634,7 @@ impl ChannelModalDelegate {
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 = picker.delegate_mut(); let this = &mut picker.delegate;
if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) { if let Some(ix) = this.members.iter_mut().position(|m| m.user.id == user_id) {
this.members.remove(ix); this.members.remove(ix);
this.matching_member_indices.retain_mut(|member_ix| { this.matching_member_indices.retain_mut(|member_ix| {
@ -683,7 +673,7 @@ impl ChannelModalDelegate {
kind: proto::channel_member::Kind::Invitee, kind: proto::channel_member::Kind::Invitee,
role: ChannelRole::Member, role: ChannelRole::Member,
}; };
let members = &mut this.delegate_mut().members; let members = &mut this.delegate.members;
match members.binary_search_by_key(&new_member.sort_key(), |k| k.sort_key()) { match members.binary_search_by_key(&new_member.sort_key(), |k| k.sort_key()) {
Ok(ix) | Err(ix) => members.insert(ix, new_member), Ok(ix) | Err(ix) => members.insert(ix, new_member),
} }
@ -695,23 +685,23 @@ impl ChannelModalDelegate {
} }
fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) { fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) {
self.context_menu.update(cx, |context_menu, cx| { // self.context_menu.update(cx, |context_menu, cx| {
context_menu.show( // context_menu.show(
Default::default(), // Default::default(),
AnchorCorner::TopRight, // AnchorCorner::TopRight,
vec![ // vec![
ContextMenuItem::action("Remove", RemoveMember), // ContextMenuItem::action("Remove", RemoveMember),
ContextMenuItem::action( // ContextMenuItem::action(
if role == ChannelRole::Admin { // if role == ChannelRole::Admin {
"Make non-admin" // "Make non-admin"
} else { // } else {
"Make admin" // "Make admin"
}, // },
ToggleMemberAdmin, // ToggleMemberAdmin,
), // ),
], // ],
cx, // cx,
) // )
}) // })
} }
} }

View file

@ -68,7 +68,7 @@ impl Render for CopilotButton {
if let Status::Error(e) = status { if let Status::Error(e) = status {
return div().child( return div().child(
IconButton::new("github-copilot", icon) IconButton::new("copilot-error", icon)
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
if let Some(workspace) = cx.window_handle().downcast::<Workspace>() { if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
@ -98,7 +98,7 @@ impl Render for CopilotButton {
let this = cx.view().clone(); let this = cx.view().clone();
div().child( div().child(
popover_menu("github-copilot") popover_menu("copilot")
.menu(move |cx| match status { .menu(move |cx| match status {
Status::Authorized => this.update(cx, |this, cx| this.build_copilot_menu(cx)), Status::Authorized => this.update(cx, |this, cx| this.build_copilot_menu(cx)),
_ => this.update(cx, |this, cx| this.build_copilot_start_menu(cx)), _ => this.update(cx, |this, cx| this.build_copilot_start_menu(cx)),

View file

@ -178,6 +178,15 @@ impl<D: PickerDelegate> Picker<D> {
} }
cx.notify(); cx.notify();
} }
pub fn query(&self, cx: &AppContext) -> String {
self.editor.read(cx).text(cx)
}
pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |editor, cx| editor.set_text(query, cx));
}
} }
impl<D: PickerDelegate> Render for Picker<D> { impl<D: PickerDelegate> Render for Picker<D> {