mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-05 10:20:51 +00:00
commit
450f2bf6b6
4 changed files with 139 additions and 31 deletions
|
@ -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 {}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue