Show channel notes in current call section of collab panel
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
1ae54ca620
commit
a327320f7d
1 changed files with 146 additions and 42 deletions
|
@ -18,13 +18,14 @@ use gpui::{
|
||||||
MouseEventHandler, Orientation, OverlayPositionMode, Padding, ParentElement, SafeStylable,
|
MouseEventHandler, Orientation, OverlayPositionMode, Padding, ParentElement, SafeStylable,
|
||||||
Stack, Svg,
|
Stack, Svg,
|
||||||
},
|
},
|
||||||
|
fonts::TextStyle,
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
impl_actions,
|
impl_actions,
|
||||||
platform::{CursorStyle, MouseButton, PromptLevel},
|
platform::{CursorStyle, MouseButton, PromptLevel},
|
||||||
serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, ModelHandle,
|
serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, FontCache, ModelHandle,
|
||||||
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
|
@ -183,6 +184,7 @@ pub struct CollabPanel {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SerializedChannelsPanel {
|
struct SerializedChannelsPanel {
|
||||||
width: Option<f32>,
|
width: Option<f32>,
|
||||||
|
collapsed_channels: Vec<ChannelId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -227,6 +229,9 @@ enum ListEntry {
|
||||||
channel: Arc<Channel>,
|
channel: Arc<Channel>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
},
|
},
|
||||||
|
ChannelNotes {
|
||||||
|
channel_id: ChannelId,
|
||||||
|
},
|
||||||
ChannelEditor {
|
ChannelEditor {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
},
|
},
|
||||||
|
@ -370,6 +375,12 @@ impl CollabPanel {
|
||||||
return channel_row;
|
return channel_row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListEntry::ChannelNotes { channel_id } => this.render_channel_notes(
|
||||||
|
*channel_id,
|
||||||
|
&theme.collab_panel,
|
||||||
|
is_selected,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
ListEntry::ChannelInvite(channel) => Self::render_channel_invite(
|
ListEntry::ChannelInvite(channel) => Self::render_channel_invite(
|
||||||
channel.clone(),
|
channel.clone(),
|
||||||
this.channel_store.clone(),
|
this.channel_store.clone(),
|
||||||
|
@ -509,6 +520,7 @@ impl CollabPanel {
|
||||||
if let Some(serialized_panel) = serialized_panel {
|
if let Some(serialized_panel) = serialized_panel {
|
||||||
panel.update(cx, |panel, cx| {
|
panel.update(cx, |panel, cx| {
|
||||||
panel.width = serialized_panel.width;
|
panel.width = serialized_panel.width;
|
||||||
|
panel.collapsed_channels = serialized_panel.collapsed_channels;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -519,12 +531,16 @@ impl CollabPanel {
|
||||||
|
|
||||||
fn serialize(&mut self, cx: &mut ViewContext<Self>) {
|
fn serialize(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let width = self.width;
|
let width = self.width;
|
||||||
|
let collapsed_channels = self.collapsed_channels.clone();
|
||||||
self.pending_serialization = cx.background().spawn(
|
self.pending_serialization = cx.background().spawn(
|
||||||
async move {
|
async move {
|
||||||
KEY_VALUE_STORE
|
KEY_VALUE_STORE
|
||||||
.write_kvp(
|
.write_kvp(
|
||||||
COLLABORATION_PANEL_KEY.into(),
|
COLLABORATION_PANEL_KEY.into(),
|
||||||
serde_json::to_string(&SerializedChannelsPanel { width })?,
|
serde_json::to_string(&SerializedChannelsPanel {
|
||||||
|
width,
|
||||||
|
collapsed_channels,
|
||||||
|
})?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
@ -548,6 +564,10 @@ impl CollabPanel {
|
||||||
if !self.collapsed_sections.contains(&Section::ActiveCall) {
|
if !self.collapsed_sections.contains(&Section::ActiveCall) {
|
||||||
let room = room.read(cx);
|
let room = room.read(cx);
|
||||||
|
|
||||||
|
if let Some(channel_id) = room.channel_id() {
|
||||||
|
self.entries.push(ListEntry::ChannelNotes { channel_id })
|
||||||
|
}
|
||||||
|
|
||||||
// Populate the active user.
|
// Populate the active user.
|
||||||
if let Some(user) = user_store.current_user() {
|
if let Some(user) = user_store.current_user() {
|
||||||
self.match_candidates.clear();
|
self.match_candidates.clear();
|
||||||
|
@ -1007,25 +1027,19 @@ impl CollabPanel {
|
||||||
) -> AnyElement<Self> {
|
) -> AnyElement<Self> {
|
||||||
enum JoinProject {}
|
enum JoinProject {}
|
||||||
|
|
||||||
let font_cache = cx.font_cache();
|
let host_avatar_width = theme
|
||||||
let host_avatar_height = theme
|
|
||||||
.contact_avatar
|
.contact_avatar
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
let row = &theme.project_row.inactive_state().default;
|
|
||||||
let tree_branch = theme.tree_branch;
|
let tree_branch = theme.tree_branch;
|
||||||
let line_height = row.name.text.line_height(font_cache);
|
|
||||||
let cap_height = row.name.text.cap_height(font_cache);
|
|
||||||
let baseline_offset =
|
|
||||||
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
|
|
||||||
let project_name = if worktree_root_names.is_empty() {
|
let project_name = if worktree_root_names.is_empty() {
|
||||||
"untitled".to_string()
|
"untitled".to_string()
|
||||||
} else {
|
} else {
|
||||||
worktree_root_names.join(", ")
|
worktree_root_names.join(", ")
|
||||||
};
|
};
|
||||||
|
|
||||||
MouseEventHandler::new::<JoinProject, _>(project_id as usize, cx, |mouse_state, _| {
|
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 tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||||
let row = theme
|
let row = theme
|
||||||
.project_row
|
.project_row
|
||||||
|
@ -1033,39 +1047,20 @@ impl CollabPanel {
|
||||||
.style_for(mouse_state);
|
.style_for(mouse_state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
|
.with_child(render_tree_branch(
|
||||||
|
tree_branch,
|
||||||
|
&row.name.text,
|
||||||
|
is_last,
|
||||||
|
vec2f(host_avatar_width, theme.row_height),
|
||||||
|
cx.font_cache(),
|
||||||
|
))
|
||||||
.with_child(
|
.with_child(
|
||||||
Stack::new()
|
Svg::new("icons/file_icons/folder.svg")
|
||||||
.with_child(Canvas::new(move |scene, bounds, _, _, _| {
|
.with_color(theme.channel_hash.color)
|
||||||
let start_x =
|
|
||||||
bounds.min_x() + (bounds.width() / 2.) - (tree_branch.width / 2.);
|
|
||||||
let end_x = bounds.max_x();
|
|
||||||
let start_y = bounds.min_y();
|
|
||||||
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
|
|
||||||
|
|
||||||
scene.push_quad(gpui::Quad {
|
|
||||||
bounds: RectF::from_points(
|
|
||||||
vec2f(start_x, start_y),
|
|
||||||
vec2f(
|
|
||||||
start_x + tree_branch.width,
|
|
||||||
if is_last { end_y } else { bounds.max_y() },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
background: Some(tree_branch.color),
|
|
||||||
border: gpui::Border::default(),
|
|
||||||
corner_radii: (0.).into(),
|
|
||||||
});
|
|
||||||
scene.push_quad(gpui::Quad {
|
|
||||||
bounds: RectF::from_points(
|
|
||||||
vec2f(start_x, end_y),
|
|
||||||
vec2f(end_x, end_y + tree_branch.width),
|
|
||||||
),
|
|
||||||
background: Some(tree_branch.color),
|
|
||||||
border: gpui::Border::default(),
|
|
||||||
corner_radii: (0.).into(),
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_width(host_avatar_height),
|
.with_width(theme.channel_hash.width)
|
||||||
|
.aligned()
|
||||||
|
.left(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(project_name, row.name.text.clone())
|
Label::new(project_name, row.name.text.clone())
|
||||||
|
@ -1240,7 +1235,7 @@ impl CollabPanel {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(name) = channel_name {
|
if let Some(name) = channel_name {
|
||||||
Cow::Owned(format!("Current Call - #{}", name))
|
Cow::Owned(format!("#{}", name))
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed("Current Call")
|
Cow::Borrowed("Current Call")
|
||||||
}
|
}
|
||||||
|
@ -1676,6 +1671,61 @@ impl CollabPanel {
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_channel_notes(
|
||||||
|
&self,
|
||||||
|
channel_id: ChannelId,
|
||||||
|
theme: &theme::CollabPanel,
|
||||||
|
is_selected: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> AnyElement<Self> {
|
||||||
|
enum ChannelNotes {}
|
||||||
|
let host_avatar_width = theme
|
||||||
|
.contact_avatar
|
||||||
|
.width
|
||||||
|
.or(theme.contact_avatar.height)
|
||||||
|
.unwrap_or(0.);
|
||||||
|
|
||||||
|
MouseEventHandler::new::<ChannelNotes, _>(channel_id as usize, cx, |state, cx| {
|
||||||
|
let tree_branch = *theme.tree_branch.in_state(is_selected).style_for(state);
|
||||||
|
let row = theme.project_row.in_state(is_selected).style_for(state);
|
||||||
|
|
||||||
|
Flex::<Self>::row()
|
||||||
|
.with_child(render_tree_branch(
|
||||||
|
tree_branch,
|
||||||
|
&row.name.text,
|
||||||
|
true,
|
||||||
|
vec2f(host_avatar_width, theme.row_height),
|
||||||
|
cx.font_cache(),
|
||||||
|
))
|
||||||
|
.with_child(
|
||||||
|
Svg::new("icons/radix/file.svg")
|
||||||
|
.with_color(theme.channel_hash.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(theme.channel_hash.width)
|
||||||
|
.aligned()
|
||||||
|
.left(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
Label::new("notes", theme.channel_name.text.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.channel_name.container)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.flex(1., true),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.row_height)
|
||||||
|
.contained()
|
||||||
|
.with_style(*theme.channel_row.style_for(is_selected, state))
|
||||||
|
.with_padding_left(theme.channel_row.default_style().padding.left)
|
||||||
|
})
|
||||||
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
this.open_channel_buffer(&OpenChannelBuffer { channel_id }, cx);
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
|
||||||
fn render_channel_invite(
|
fn render_channel_invite(
|
||||||
channel: Arc<Channel>,
|
channel: Arc<Channel>,
|
||||||
channel_store: ModelHandle<ChannelStore>,
|
channel_store: ModelHandle<ChannelStore>,
|
||||||
|
@ -2114,6 +2164,7 @@ impl CollabPanel {
|
||||||
self.collapsed_channels.insert(ix, channel_id);
|
self.collapsed_channels.insert(ix, channel_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
self.serialize(cx);
|
||||||
self.update_entries(true, cx);
|
self.update_entries(true, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
|
@ -2392,6 +2443,51 @@ impl CollabPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_tree_branch(
|
||||||
|
branch_style: theme::TreeBranch,
|
||||||
|
row_style: &TextStyle,
|
||||||
|
is_last: bool,
|
||||||
|
size: Vector2F,
|
||||||
|
font_cache: &FontCache,
|
||||||
|
) -> gpui::elements::ConstrainedBox<CollabPanel> {
|
||||||
|
let line_height = row_style.line_height(font_cache);
|
||||||
|
let cap_height = row_style.cap_height(font_cache);
|
||||||
|
let baseline_offset = row_style.baseline_offset(font_cache) + (size.y() - line_height) / 2.;
|
||||||
|
|
||||||
|
Canvas::new(move |scene, bounds, _, _, _| {
|
||||||
|
scene.paint_layer(None, |scene| {
|
||||||
|
let start_x = bounds.min_x() + (bounds.width() / 2.) - (branch_style.width / 2.);
|
||||||
|
let end_x = bounds.max_x();
|
||||||
|
let start_y = bounds.min_y();
|
||||||
|
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
|
||||||
|
|
||||||
|
scene.push_quad(gpui::Quad {
|
||||||
|
bounds: RectF::from_points(
|
||||||
|
vec2f(start_x, start_y),
|
||||||
|
vec2f(
|
||||||
|
start_x + branch_style.width,
|
||||||
|
if is_last { end_y } else { bounds.max_y() },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
background: Some(branch_style.color),
|
||||||
|
border: gpui::Border::default(),
|
||||||
|
corner_radii: (0.).into(),
|
||||||
|
});
|
||||||
|
scene.push_quad(gpui::Quad {
|
||||||
|
bounds: RectF::from_points(
|
||||||
|
vec2f(start_x, end_y),
|
||||||
|
vec2f(end_x, end_y + branch_style.width),
|
||||||
|
),
|
||||||
|
background: Some(branch_style.color),
|
||||||
|
border: gpui::Border::default(),
|
||||||
|
corner_radii: (0.).into(),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.constrained()
|
||||||
|
.with_width(size.x())
|
||||||
|
}
|
||||||
|
|
||||||
impl View for CollabPanel {
|
impl View for CollabPanel {
|
||||||
fn ui_name() -> &'static str {
|
fn ui_name() -> &'static str {
|
||||||
"CollabPanel"
|
"CollabPanel"
|
||||||
|
@ -2601,6 +2697,14 @@ impl PartialEq for ListEntry {
|
||||||
return channel_1.id == channel_2.id && depth_1 == depth_2;
|
return channel_1.id == channel_2.id && depth_1 == depth_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListEntry::ChannelNotes { channel_id } => {
|
||||||
|
if let ListEntry::ChannelNotes {
|
||||||
|
channel_id: other_id,
|
||||||
|
} = other
|
||||||
|
{
|
||||||
|
return channel_id == other_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
ListEntry::ChannelInvite(channel_1) => {
|
ListEntry::ChannelInvite(channel_1) => {
|
||||||
if let ListEntry::ChannelInvite(channel_2) = other {
|
if let ListEntry::ChannelInvite(channel_2) = other {
|
||||||
return channel_1.id == channel_2.id;
|
return channel_1.id == channel_2.id;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue