More progress on collab panel (#3426)

Release Notes:

- n/a
This commit is contained in:
Conrad Irwin 2023-11-28 15:30:51 -07:00 committed by GitHub
commit 450f2bf6b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 139 additions and 31 deletions

View file

@ -17,6 +17,7 @@ mod contact_finder;
// Client, Contact, User, UserStore, // Client, Contact, User, UserStore,
// }; // };
use contact_finder::ContactFinder; use contact_finder::ContactFinder;
use rpc::proto;
// use context_menu::{ContextMenu, ContextMenuItem}; // use context_menu::{ContextMenu, ContextMenuItem};
// use db::kvp::KEY_VALUE_STORE; // use db::kvp::KEY_VALUE_STORE;
// use drag_and_drop::{DragAndDrop, Draggable}; // use drag_and_drop::{DragAndDrop, Draggable};
@ -166,15 +167,17 @@ use editor::Editor;
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt}; use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{ use gpui::{
actions, div, img, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle, actions, div, img, prelude::*, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter,
Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement,
RenderOnce, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView, Render, RenderOnce, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
WeakView,
}; };
use project::Fs; use project::Fs;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;
use ui::{ use ui::{
h_stack, v_stack, Avatar, Button, Icon, IconButton, Label, List, ListHeader, ListItem, Tooltip, h_stack, v_stack, Avatar, Button, Color, Icon, IconButton, Label, List, ListHeader, ListItem,
Toggle, Tooltip,
}; };
use util::{maybe, ResultExt}; use util::{maybe, ResultExt};
use workspace::{ use workspace::{
@ -183,7 +186,7 @@ use workspace::{
Workspace, Workspace,
}; };
use crate::CollaborationPanelSettings; use crate::{face_pile::FacePile, CollaborationPanelSettings};
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, _| {
@ -309,7 +312,7 @@ pub struct CollabPanel {
// channel_name_editor: ViewHandle<Editor>, // channel_name_editor: ViewHandle<Editor>,
channel_editing_state: Option<ChannelEditingState>, channel_editing_state: Option<ChannelEditingState>,
entries: Vec<ListEntry>, entries: Vec<ListEntry>,
// selection: Option<usize>, selection: Option<usize>,
channel_store: Model<ChannelStore>, channel_store: Model<ChannelStore>,
user_store: Model<UserStore>, user_store: Model<UserStore>,
client: Arc<Client>, client: Arc<Client>,
@ -600,7 +603,7 @@ impl CollabPanel {
filter_editor, filter_editor,
entries: Vec::default(), entries: Vec::default(),
channel_editing_state: None, channel_editing_state: None,
// selection: None, selection: None,
channel_store: ChannelStore::global(cx), channel_store: ChannelStore::global(cx),
user_store: workspace.user_store().clone(), user_store: workspace.user_store().clone(),
// project: workspace.project().clone(), // project: workspace.project().clone(),
@ -2357,14 +2360,14 @@ impl CollabPanel {
// self.deploy_channel_context_menu(None, &channel.clone(), self.selection.unwrap(), cx); // self.deploy_channel_context_menu(None, &channel.clone(), self.selection.unwrap(), cx);
// } // }
// fn selected_channel(&self) -> Option<&Arc<Channel>> { fn selected_channel(&self) -> Option<&Arc<Channel>> {
// self.selection self.selection
// .and_then(|ix| self.entries.get(ix)) .and_then(|ix| self.entries.get(ix))
// .and_then(|entry| match entry { .and_then(|entry| match entry {
// ListEntry::Channel { channel, .. } => Some(channel), ListEntry::Channel { channel, .. } => Some(channel),
// _ => None, _ => None,
// }) })
// } }
// fn show_channel_modal( // fn show_channel_modal(
// &mut self, // &mut self,
@ -3019,7 +3022,123 @@ impl CollabPanel {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl IntoElement { ) -> impl IntoElement {
let channel_id = channel.id; let channel_id = channel.id;
ListItem::new(channel_id as usize).child(Label::new(channel.name.clone()))
let is_public = self
.channel_store
.read(cx)
.channel_for_id(channel_id)
.map(|channel| channel.visibility)
== Some(proto::ChannelVisibility::Public);
let other_selected = self.selected_channel().map(|channel| channel.id) == Some(channel.id);
let disclosed = has_children
.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok())
.unwrap_or(false);
let is_active = maybe!({
let call_channel = ActiveCall::global(cx)
.read(cx)
.room()?
.read(cx)
.channel_id()?;
Some(call_channel == channel_id)
})
.unwrap_or(false);
let has_messages_notification = channel.unseen_message_id.is_some() || true;
let has_notes_notification = channel.unseen_note_version.is_some();
const FACEPILE_LIMIT: usize = 3;
let participants = self.channel_store.read(cx).channel_participants(channel_id);
let face_pile = if !participants.is_empty() {
let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
let result = FacePile {
faces: participants
.iter()
.filter_map(|user| Some(Avatar::data(user.avatar.clone()?).into_any_element()))
.take(FACEPILE_LIMIT)
.chain(if extra_count > 0 {
Some(Label::new(format!("+{}", extra_count)).into_any_element())
} else {
None
})
.collect::<Vec<_>>(),
};
Some(result)
} else {
None
};
div().group("").child(
ListItem::new(channel_id as usize)
.indent_level(depth)
.left_icon(if is_public { Icon::Public } else { Icon::Hash })
.selected(is_selected || is_active)
.child(
h_stack()
.w_full()
.justify_between()
.child(
div()
.id(channel_id as usize)
.child(Label::new(channel.name.clone()))
.children(face_pile.map(|face_pile| face_pile.render(cx)))
.tooltip(|cx| Tooltip::text("Join channel", cx)),
)
.child(
h_stack()
.child(
div()
.id("channel_chat")
.bg(gpui::blue())
.when(!has_messages_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
IconButton::new("test_chat", Icon::MessageBubbles)
.color(if has_messages_notification {
Color::Default
} else {
Color::Muted
}),
)
.tooltip(|cx| Tooltip::text("Open channel chat", cx)),
)
.child(
div()
.id("channel_notes")
.when(!has_notes_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
div().child("Notes").id("test_notes").tooltip(|cx| {
Tooltip::text("Open channel notes", cx)
}),
), // .child(
// IconButton::new("channel_notes", Icon::File)
// .color(if has_notes_notification {
// Color::Default
// } else {
// Color::Muted
// })
// .tooltip(|cx| {
// Tooltip::text("Open channel notes", cx)
// }),
// ),
),
),
)
.toggle(if has_children {
Toggle::Toggled(disclosed)
} else {
Toggle::NotToggleable
})
.on_click(cx.listener(|this, _, cx| todo!()))
.on_secondary_mouse_down(cx.listener(|this, _, cx| {
todo!() // open context menu
})),
)
// let channel_id = channel.id; // let channel_id = channel.id;
// let collab_theme = &theme.collab_panel; // let collab_theme = &theme.collab_panel;
// let is_public = self // let is_public = self
@ -3032,18 +3151,6 @@ impl CollabPanel {
// let disclosed = // let disclosed =
// has_children.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok()); // has_children.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok());
// let is_active = maybe!({
// let call_channel = ActiveCall::global(cx)
// .read(cx)
// .room()?
// .read(cx)
// .channel_id()?;
// Some(call_channel == channel_id)
// })
// .unwrap_or(false);
// const FACEPILE_LIMIT: usize = 3;
// enum ChannelCall {} // enum ChannelCall {}
// enum ChannelNote {} // enum ChannelNote {}
// enum NotesTooltip {} // enum NotesTooltip {}

View file

@ -3,8 +3,8 @@ use gpui::{
}; };
#[derive(Default)] #[derive(Default)]
pub(crate) struct FacePile { pub struct FacePile {
faces: Vec<AnyElement>, pub faces: Vec<AnyElement>,
} }
impl RenderOnce for FacePile { impl RenderOnce for FacePile {

View file

@ -105,7 +105,6 @@ impl Element for Overlay {
origin: Point::zero(), origin: Point::zero(),
size: cx.viewport_size(), size: cx.viewport_size(),
}; };
dbg!(limits);
match self.fit_mode { match self.fit_mode {
OverlayFitMode::SnapToWindow => { OverlayFitMode::SnapToWindow => {

View file

@ -63,6 +63,7 @@ pub enum Icon {
Mic, Mic,
MicMute, MicMute,
Plus, Plus,
Public,
Quote, Quote,
Replace, Replace,
ReplaceAll, ReplaceAll,
@ -134,6 +135,7 @@ impl Icon {
Icon::Mic => "icons/mic.svg", Icon::Mic => "icons/mic.svg",
Icon::MicMute => "icons/mic-mute.svg", Icon::MicMute => "icons/mic-mute.svg",
Icon::Plus => "icons/plus.svg", Icon::Plus => "icons/plus.svg",
Icon::Public => "icons/public.svg",
Icon::Quote => "icons/quote.svg", Icon::Quote => "icons/quote.svg",
Icon::Replace => "icons/replace.svg", Icon::Replace => "icons/replace.svg",
Icon::ReplaceAll => "icons/replace_all.svg", Icon::ReplaceAll => "icons/replace_all.svg",