@@ -1,17 +1,99 @@
use gpui::{
- elements::Empty, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
+ elements::*, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
};
+use postage::watch;
-use crate::user::UserStore;
+use crate::{
+ theme::Theme,
+ user::{Collaborator, UserStore},
+ Settings,
+};
pub struct PeoplePanel {
+ collaborators: ListState,
user_store: ModelHandle<UserStore>,
}
impl PeoplePanel {
- pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
- cx.observe(&user_store, |_, _, cx| cx.notify());
- Self { user_store }
+ pub fn new(
+ user_store: ModelHandle<UserStore>,
+ settings: watch::Receiver<Settings>,
+ cx: &mut ViewContext<Self>,
+ ) -> Self {
+ cx.observe(&user_store, Self::update_collaborators);
+ Self {
+ collaborators: ListState::new(
+ user_store.read(cx).collaborators().len(),
+ Orientation::Top,
+ 1000.,
+ {
+ let user_store = user_store.clone();
+ move |ix, cx| {
+ let user_store = user_store.read(cx);
+ let settings = settings.borrow();
+ Self::render_collaborator(&user_store.collaborators()[ix], &settings.theme)
+ }
+ },
+ ),
+ user_store,
+ }
+ }
+
+ fn update_collaborators(&mut self, _: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) {
+ self.collaborators
+ .reset(self.user_store.read(cx).collaborators().len());
+ cx.notify();
+ }
+
+ fn render_collaborator(collaborator: &Collaborator, theme: &Theme) -> ElementBox {
+ Flex::column()
+ .with_child(
+ Flex::row()
+ .with_children(collaborator.user.avatar.clone().map(|avatar| {
+ ConstrainedBox::new(
+ Image::new(avatar)
+ .with_style(theme.people_panel.worktree_host_avatar)
+ .boxed(),
+ )
+ .with_width(20.)
+ .boxed()
+ }))
+ .with_child(
+ Label::new(
+ collaborator.user.github_login.clone(),
+ theme.people_panel.collaborator_username.clone(),
+ )
+ .boxed(),
+ )
+ .boxed(),
+ )
+ .with_children(collaborator.worktrees.iter().map(|worktree| {
+ Flex::row()
+ .with_child(
+ Container::new(
+ Label::new(
+ worktree.root_name.clone(),
+ theme.people_panel.worktree_name.text.clone(),
+ )
+ .boxed(),
+ )
+ .with_style(theme.people_panel.worktree_name.container)
+ .boxed(),
+ )
+ .with_children(worktree.participants.iter().filter_map(|participant| {
+ participant.avatar.clone().map(|avatar| {
+ ConstrainedBox::new(
+ Image::new(avatar)
+ .with_style(theme.people_panel.worktree_guest_avatar)
+ .boxed(),
+ )
+ .with_width(16.)
+ .boxed()
+ })
+ }))
+ .boxed()
+ }))
+ .boxed()
}
}
@@ -26,7 +108,7 @@ impl View for PeoplePanel {
"PeoplePanel"
}
- fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
- Empty::new().boxed()
+ fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+ List::new(self.collaborators.clone()).boxed()
}
}
@@ -23,6 +23,7 @@ pub struct Theme {
pub name: String,
pub workspace: Workspace,
pub chat_panel: ChatPanel,
+ pub people_panel: PeoplePanel,
pub selector: Selector,
pub editor: EditorStyle,
pub syntax: SyntaxTheme,
@@ -103,6 +104,16 @@ pub struct ChatPanel {
pub hovered_sign_in_prompt: TextStyle,
}
+#[derive(Deserialize)]
+pub struct PeoplePanel {
+ #[serde(flatten)]
+ pub container: ContainerStyle,
+ pub collaborator_username: TextStyle,
+ pub worktree_name: ContainedText,
+ pub worktree_host_avatar: ImageStyle,
+ pub worktree_guest_avatar: ImageStyle,
+}
+
#[derive(Deserialize)]
pub struct ChatMessage {
#[serde(flatten)]
@@ -381,8 +381,10 @@ impl Workspace {
);
right_sidebar.add_item(
"icons/user-16.svg",
- cx.add_view(|cx| PeoplePanel::new(app_state.user_store.clone(), cx))
- .into(),
+ cx.add_view(|cx| {
+ PeoplePanel::new(app_state.user_store.clone(), app_state.settings.clone(), cx)
+ })
+ .into(),
);
let mut current_user = app_state.user_store.read(cx).watch_current_user().clone();