Allow joining worktrees by clicking them in the people panel
Co-Authored-By: Antonio Scandurra <me@as-cii.com> Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Antonio Scandurra
Nathan Sobo
parent
65a3af9bde
commit
412535420b
@@ -202,6 +202,7 @@ impl Store {
|
||||
worktrees: Vec::new(),
|
||||
});
|
||||
host.worktrees.push(proto::WorktreeMetadata {
|
||||
id: *worktree_id,
|
||||
root_name: worktree.root_name.clone(),
|
||||
is_shared: worktree.share().is_ok(),
|
||||
guests: guests.into_iter().collect(),
|
||||
|
||||
@@ -125,14 +125,20 @@ color = "$text.1.color"
|
||||
|
||||
[people_panel]
|
||||
extends = "$panel"
|
||||
host_avatar = { corner_radius = 10, width = 20 }
|
||||
host_username = { extends = "$text.0", padding.left = 5 }
|
||||
worktree_host_avatar = { corner_radius = 10, width = 20 }
|
||||
worktree_guest_avatar = { corner_radius = 8, width = 16 }
|
||||
guest_avatar = { corner_radius = 8, width = 16 }
|
||||
|
||||
[people_panel.worktree_name]
|
||||
extends = "$text.0"
|
||||
[people_panel.worktree]
|
||||
extends = "$text.1"
|
||||
padding = { left = 5 }
|
||||
|
||||
[people_panel.hovered_worktree]
|
||||
extends = "$text.0"
|
||||
padding = { left = 8 }
|
||||
# extends = "$people_panel.worktree"
|
||||
# color = "$text.0.color"
|
||||
|
||||
[people_panel.tree_branch]
|
||||
width = 1
|
||||
color = "$surface.2"
|
||||
|
||||
@@ -26,11 +26,6 @@ pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
|
||||
keystroke: None,
|
||||
action: Box::new(workspace::ShareWorktree),
|
||||
},
|
||||
MenuItem::Action {
|
||||
name: "Join",
|
||||
keystroke: None,
|
||||
action: Box::new(workspace::JoinWorktree(state.clone())),
|
||||
},
|
||||
MenuItem::Action {
|
||||
name: "Quit",
|
||||
keystroke: Some("cmd-q"),
|
||||
|
||||
+42
-25
@@ -4,13 +4,17 @@ use crate::{
|
||||
Settings,
|
||||
};
|
||||
use gpui::{
|
||||
action,
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
Element, ElementBox, Entity, FontCache, ModelHandle, RenderContext, Subscription, View,
|
||||
platform::CursorStyle,
|
||||
Element, ElementBox, Entity, LayoutContext, ModelHandle, RenderContext, Subscription, View,
|
||||
ViewContext,
|
||||
};
|
||||
use postage::watch;
|
||||
|
||||
action!(JoinWorktree, u64);
|
||||
|
||||
pub struct PeoplePanel {
|
||||
collaborators: ListState,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
@@ -33,11 +37,8 @@ impl PeoplePanel {
|
||||
let user_store = user_store.clone();
|
||||
let settings = settings.clone();
|
||||
move |ix, cx| {
|
||||
Self::render_collaborator(
|
||||
&user_store.read(cx).collaborators()[ix],
|
||||
&settings.borrow().theme,
|
||||
cx.font_cache(),
|
||||
)
|
||||
let collaborators = user_store.read(cx).collaborators().clone();
|
||||
Self::render_collaborator(&collaborators[ix], &settings.borrow().theme, cx)
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -56,23 +57,26 @@ impl PeoplePanel {
|
||||
fn render_collaborator(
|
||||
collaborator: &Collaborator,
|
||||
theme: &Theme,
|
||||
font_cache: &FontCache,
|
||||
cx: &mut LayoutContext,
|
||||
) -> ElementBox {
|
||||
let theme = &theme.people_panel;
|
||||
let worktree_count = collaborator.worktrees.len();
|
||||
let line_height = theme.worktree_name.text.line_height(font_cache);
|
||||
let cap_height = theme.worktree_name.text.cap_height(font_cache);
|
||||
let baseline_offset = theme.worktree_name.text.baseline_offset(font_cache);
|
||||
let font_cache = cx.font_cache();
|
||||
let line_height = theme.worktree.text.line_height(font_cache);
|
||||
let cap_height = theme.worktree.text.cap_height(font_cache);
|
||||
let baseline_offset = theme.worktree.text.baseline_offset(font_cache);
|
||||
let tree_branch = theme.tree_branch;
|
||||
|
||||
Flex::column()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_children(collaborator.user.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(theme.worktree_host_avatar)
|
||||
.boxed()
|
||||
}))
|
||||
.with_children(
|
||||
collaborator
|
||||
.user
|
||||
.avatar
|
||||
.clone()
|
||||
.map(|avatar| Image::new(avatar).with_style(theme.host_avatar).boxed()),
|
||||
)
|
||||
.with_child(
|
||||
Container::new(
|
||||
Label::new(
|
||||
@@ -92,6 +96,7 @@ impl PeoplePanel {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ix, worktree)| {
|
||||
let worktree_id = worktree.id;
|
||||
Flex::row()
|
||||
.with_child(
|
||||
ConstrainedBox::new(
|
||||
@@ -136,21 +141,33 @@ impl PeoplePanel {
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Container::new(
|
||||
Label::new(
|
||||
worktree.root_name.clone(),
|
||||
theme.worktree_name.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
MouseEventHandler::new::<PeoplePanel, _, _, _>(
|
||||
worktree_id as usize,
|
||||
cx,
|
||||
|mouse_state, _| {
|
||||
let style = if mouse_state.hovered {
|
||||
&theme.hovered_worktree
|
||||
} else {
|
||||
&theme.worktree
|
||||
};
|
||||
Container::new(
|
||||
Label::new(
|
||||
worktree.root_name.clone(),
|
||||
style.text.clone(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(style.container)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
.with_style(theme.worktree_name.container)
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(move |cx| cx.dispatch_action(JoinWorktree(worktree_id)))
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(worktree.guests.iter().filter_map(|participant| {
|
||||
participant.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(theme.worktree_guest_avatar)
|
||||
.boxed()
|
||||
Image::new(avatar).with_style(theme.guest_avatar).boxed()
|
||||
})
|
||||
}))
|
||||
.boxed()
|
||||
|
||||
+5
-4
@@ -109,10 +109,11 @@ pub struct ChatPanel {
|
||||
pub struct PeoplePanel {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub host_avatar: ImageStyle,
|
||||
pub host_username: ContainedText,
|
||||
pub worktree_name: ContainedText,
|
||||
pub worktree_host_avatar: ImageStyle,
|
||||
pub worktree_guest_avatar: ImageStyle,
|
||||
pub worktree: ContainedText,
|
||||
pub hovered_worktree: ContainedText,
|
||||
pub guest_avatar: ImageStyle,
|
||||
pub tree_branch: TreeBranch,
|
||||
}
|
||||
|
||||
@@ -161,7 +162,7 @@ pub struct Selector {
|
||||
pub active_item: ContainedLabel,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ContainedText {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
|
||||
+6
-4
@@ -28,6 +28,7 @@ pub struct Collaborator {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WorktreeMetadata {
|
||||
pub id: u64,
|
||||
pub root_name: String,
|
||||
pub is_shared: bool,
|
||||
pub guests: Vec<Arc<User>>,
|
||||
@@ -36,7 +37,7 @@ pub struct WorktreeMetadata {
|
||||
pub struct UserStore {
|
||||
users: HashMap<u64, Arc<User>>,
|
||||
current_user: watch::Receiver<Option<Arc<User>>>,
|
||||
collaborators: Vec<Collaborator>,
|
||||
collaborators: Arc<[Collaborator]>,
|
||||
rpc: Arc<Client>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
_maintain_collaborators: Task<()>,
|
||||
@@ -64,7 +65,7 @@ impl UserStore {
|
||||
Self {
|
||||
users: Default::default(),
|
||||
current_user: current_user_rx,
|
||||
collaborators: Default::default(),
|
||||
collaborators: Arc::from([]),
|
||||
rpc: rpc.clone(),
|
||||
http,
|
||||
_maintain_collaborators: cx.spawn_weak(|this, mut cx| async move {
|
||||
@@ -127,7 +128,7 @@ impl UserStore {
|
||||
}
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.collaborators = collaborators;
|
||||
this.collaborators = collaborators.into();
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
@@ -135,7 +136,7 @@ impl UserStore {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn collaborators(&self) -> &[Collaborator] {
|
||||
pub fn collaborators(&self) -> &Arc<[Collaborator]> {
|
||||
&self.collaborators
|
||||
}
|
||||
|
||||
@@ -235,6 +236,7 @@ impl Collaborator {
|
||||
);
|
||||
}
|
||||
worktrees.push(WorktreeMetadata {
|
||||
id: worktree.id,
|
||||
root_name: worktree.root_name,
|
||||
is_shared: worktree.is_shared,
|
||||
guests,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
editor::Buffer,
|
||||
fs::Fs,
|
||||
language::LanguageRegistry,
|
||||
people_panel::PeoplePanel,
|
||||
people_panel::{JoinWorktree, PeoplePanel},
|
||||
project_browser::ProjectBrowser,
|
||||
rpc,
|
||||
settings::Settings,
|
||||
@@ -44,7 +44,6 @@ action!(Open, Arc<AppState>);
|
||||
action!(OpenPaths, OpenParams);
|
||||
action!(OpenNew, Arc<AppState>);
|
||||
action!(ShareWorktree);
|
||||
action!(JoinWorktree, Arc<AppState>);
|
||||
action!(Save);
|
||||
action!(DebugElements);
|
||||
|
||||
@@ -59,6 +58,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(Workspace::open_new_file);
|
||||
cx.add_action(Workspace::share_worktree);
|
||||
cx.add_action(Workspace::toggle_sidebar_item);
|
||||
cx.add_action(Workspace::join_worktree);
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("cmd-s", Save, None),
|
||||
Binding::new("cmd-alt-i", DebugElements, None),
|
||||
@@ -839,14 +839,16 @@ impl Workspace {
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn join_worktree(&mut self, id: u64, cx: &mut ViewContext<Self>) {
|
||||
fn join_worktree(&mut self, action: &JoinWorktree, cx: &mut ViewContext<Self>) {
|
||||
let rpc = self.rpc.clone();
|
||||
let languages = self.languages.clone();
|
||||
let worktree_id = action.0;
|
||||
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
rpc.authenticate_and_connect(&cx).await?;
|
||||
let worktree = Worktree::open_remote(rpc.clone(), id, languages, &mut cx).await?;
|
||||
let worktree =
|
||||
Worktree::open_remote(rpc.clone(), worktree_id, languages, &mut cx).await?;
|
||||
this.update(&mut cx, |workspace, cx| {
|
||||
cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
|
||||
workspace.worktrees.insert(worktree);
|
||||
|
||||
@@ -343,7 +343,8 @@ message Collaborator {
|
||||
}
|
||||
|
||||
message WorktreeMetadata {
|
||||
string root_name = 1;
|
||||
bool is_shared = 2;
|
||||
repeated uint64 guests = 3;
|
||||
uint64 id = 1;
|
||||
string root_name = 2;
|
||||
bool is_shared = 3;
|
||||
repeated uint64 guests = 4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user